Algoritmos y Estructuras de Datos: Resolución de Casos Prácticos de Competencia

Cálculo Directo de Porcentajes

Implementación básica para determinar la tasa de reducción aplicando operaciones aritméticas directas sobre los valores originales y abonados. Se prioriza la precisión decimal mediante conversión explícita a tipos flotantes antes de finalizar el cálculo.

#include <iostream>
#include <iomanip>

int main() {
    int precioBase, valorAbonado;
    std::cin >> precioBase >> valorAbonado;
    
    double reduction = 100.0 * static_cast<double>(precioBase - valorAbonado) / precioBase;
    std::cout << std::fixed << std::setprecision(2) << reduction << '\n';
    
    return 0;
}

Ordenamiento y Selección Condicional

Estructura de datos ligera que almacena requerimientos temporales y costos asociados. Tras ordenar ascendente por métrica económica, se evalúa secuencialmente hasta localizar la primera opción que satisface la limitante de duración permitida.

#include <iostream>
#include <vector>
#include <algorithm>

struct Opcion { int tiempo, costo, duracion; };

bool criterio(const Opcion& a, const Opcion& b) {
    return a.costo < b.costo;
}

int main() {
    int cantidad;
    std::cin >> cantidad;
    
    std::vector<Opcion> opciones(cantidad);
    for(auto &o : opciones) {
        std::cin >> o.tiempo >> o.costo >> o.duracion;
    }

    std::sort(opciones.begin(), opciones.end(), criterio);
    
    int mejorCosto = -1;
    for(const auto &o : opciones) {
        int umbral = (o.tiempo + 1) / 2;
        if(umbral < o.duracion) {
            mejorCosto = o.costo;
            break;
        }
    }

    std::cout << mejorCosto << '\n';
    return 0;
}

Detección de Potencias Completas mediante HashSets

Generación sistemática de valores elevados a bases mayores a uno. Se emplea una colección hash para filtrar duplicados automáticamente, garantizando que cada potencial único sea contabilizado una sola vez antes de realizar la resta respecto al límite superior.

#include <iostream>
#include <unordered_set>

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    long long limite;
    std::cin >> limite;
    
    std::unordered_set<long long> visitadas;
    
    for(long long base = 2; base * base <= limite; ++base) {
        long long potencia = base * base;
        while(potencia <= limite) {
            visitadas.insert(potencia);
            if(limite / base < potencia) break;
            potencia *= base;
        }
    }

    std::cout << (limite - static_cast<long long>(visitadas.size())) << '\n';
    return 0;
}

Evaluación Probabilística con Actualización Dinámica

Modelado de escenario competitivo donde dos participantes poseen combinaciones iniciales. Se iteran todas las distribuciones posibles de cartas restantes, ajustando puntajes locales mediante cálculo incremental para evitar reconstrucciones completas y mejorar el rendimiento general.

#include <iostream>
#include <iomanip>
#include <string>
#include <cmath>

int calcularPuntaje(const std::string& mano, const int& inventario[]) {
    int frecuencias[10] = {0};
    for(char c : mano) frecuencias[c - '0']++;
    
    int total = 0;
    for(int d = 1; d <= 9; ++d) {
        int factor = std::pow(10, frecuencias[d]);
        total += d * factor;
    }
    return total;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int k;
    std::string m1, m2;
    std::cin >> k >> m1 >> m2;
    
    int reservas[10] = {0};
    for(int i = 1; i <= k; ++i) reservas[i] = 4;
    for(char c : m1) reservas[c - '0']--;
    for(char c : m2) reservas[c - '0']--;
    
    int baseScoreA = calcularPuntaje(m1, reservas);
    int baseScoreB = calcularPuntaje(m2, reservas);
    
    long long combinacionesVitoria = 0;
    long long combinacionesTotales = 0;
    
    for(int sa = 1; sa <= 9; ++sa) {
        for(int sb = 1; sb <= 9; ++sb) {
            if(reservas[sa] == 0 || (sa == sb && reservas[sb] <= 0)) continue;
            
            int invAux[10];
            std::copy(reservas, reservas + 10, invAux);
            invAux[sa]--; invAux[sb]--;
            
            int nuevoA = calcularPuntaje(m1, invAux);
            int nuevoB = calcularPuntaje(m2, invAux);
            
            if(nuevoA > nuevoB) {
                combinacionesVitoria += (sa == sb) ? (long long)reservas[sa] - 1 : (long long)reservas[sa];
            }
            combinacionesTotales += (sa == sb) ? (long long)reservas[sa] : (long long)reservas[sa] * reservas[sb];
        }
    }
    
    double probabilidad = static_cast<double>(combinacionesVitoria) / combinacionesTotales;
    std::cout << std::fixed << std::setprecision(6) << probabilidad << '\n';
    return 0;
}

Sincronización Temporal mediante Teoría de Números

Resolución de sistemas de congruencias con módulos variables. Se aplica versión extendida del Teorema Chino del Resto para fusionar periodicidades dentro de un rango acotado, implementando multiplicación modular segura para prevenir saturación de memoria.

#include <iostream>
#include <algorithm>

typedef long long ll;

ll mulSafe(ll a, ll b, ll m) {
    ll q = static_cast<ll>(static_cast<long double>(a) * b / m);
    ll r = a * b - q * m;
    return (r % m + m) % m;
}

ll extGcd(ll a, ll b, ll &x, ll &y) {
    if(b == 0) { x = 1; y = 0; return a; }
    ll x1, y1;
    ll g = extGcd(b, a % b, x1, y1);
    x = y1; y = x1 - (a / b) * y1;
    return g;
}

bool resolver(ll r1, ll m1, ll r2, ll m2, ll &rOut, ll &mOut) {
    ll x, y;
    ll g = extGcd(m1, m2, x, y);
    if((r2 - r1) % g != 0) return false;
    
    ll lcm = m1 / g * m2;
    ll coef = ((r2 - r1) / g % m2 * x % m2 + m2) % m2;
    rOut = (r1 + mulSafe(m1, coef, lcm) % lcm + lcm) % lcm;
    mOut = lcm;
    return true;
}

int main() {
    int casos;
    std::cin >> casos;
    while(casos--) {
        ll X, Y, P, Q;
        std::cin >> X >> Y >> P >> Q;
        
        ll respuesta = -1;
        
        for(ll rem1 = X; rem1 < X + Y; ++rem1) {
            for(ll rem2 = P; rem2 < P + Q; ++rem2) {
                ll rComp, mComp;
                if(resolver(rem1 % (2*Y), 2*Y, rem2 % Q, Q, rComp, mComp)) {
                    if(respuesta == -1 || rComp < respuesta) respuesta = rComp;
                }
            }
        }
        
        std::cout << (respuesta == -1 ? "infinity\n" : std::to_string(respuesta) + "\n");
    }
    return 0;
}

Modelado de Redes y Optimización por Corte Mínimo

Transformación topológica de cuadrícula hacia grafo dirigido. Se invierte la clasificación de nodos según paridad de coordenadas para estandarizar restricciones. Posterior implementación de flujo máximo tipo Dinic determina la capacidad de separación óptima, cuya resta genera la brecha buscada.

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>

#define CAP_INF 1000000007

struct Arista { int destino; long long capacidad; long long flujoActual; };
std::vector<Arista> adyacencia[3005];
int nivel[3005], puntero[3005];
int origen, sumidero, totalNodos;

void añadirArista(int u, int v, long long cap) {
    adyacencia[u].push_back({v, cap, 0});
    adyacencia[v].push_back({u, 0, 0});
}

bool buscarNiveles() {
    std::memset(nivel, -1, sizeof(nivel));
    std::queue<int> cola;
    cola.push(origen);
    nivel[origen] = 0;
    
    while(!cola.empty()) {
        int u = cola.front(); cola.pop();
        for(const auto &a : adyacencia[u]) {
            if(a.capacidad - a.flujoActual > 0 && nivel[a.destino] == -1) {
                nivel[a.destino] = nivel[u] + 1;
                cola.push(a.destino);
            }
        }
    }
    return nivel[sumidero] != -1;
}

long long entregarFlujo(int u, long long arrastrado) {
    if(!arrastrado || u == sumidero) return arrastrado;
    
    for(int &i = puntero[u]; i < adyacencia[u].size(); ++i) {
        Arista &ar = adyacencia[u][i];
        if(nivel[ar.destino] == nivel[u] + 1 && ar.capacidad - ar.flujoActual > 0) {
            long long enviado = entregarFlujo(ar.destino, std::min(arrastrado, ar.capacidad - ar.flujoActual));
            if(enviado) {
                ar.flujoActual += enviado;
                adyacencia[ar.destino][i ^ 1].flujoActual -= enviado;
                return enviado;
            }
        }
    }
    return 0;
}

long long calcularDinic() {
    long long flujoTotal = 0;
    while(buscarNiveles()) {
        std::memcpy(puntero, adyacencia, sizeof(adyacencia));
        while(long long delta = entregarFlujo(origen, CAP_INF)) flujoTotal += delta;
    }
    return flujoTotal;
}

int indexar(int fila, int col, int dim) {
    return (fila - 1) * dim + col;
}

int main() {
    int dimension;
    std::cin >> dimension;
    
    origen = 0; sumidero = dimension * dimension + 1; totalNodos = sumidero + 1;
    
    for(int f = 1; f <= dimension; ++f) {
        for(int c = 1; c <= dimension; ++c) {
            char tipo; std::cin >> tipo;
            int id = indexar(f, c, dimension);
            if(tipo != '?') {
                if((f + c) % 2 == 0) tipo = (tipo == 'W' ? 'B' : 'W');
                if(tipo == 'B') añadirArista(origen, id, CAP_INF);
                else añadirArista(id, sumidero, CAP_INF);
            }
        }
    }
    
    for(int f = 1; f <= dimension; ++f)
        for(int c = 1; c < dimension; ++c) {
            int u = indexar(f, c, dimension), v = u + 1;
            añadirArista(u, v, 1); añadirArista(v, u, 1);
        }
    for(int f = 1; f < dimension; ++f)
        for(int c = 1; c <= dimension; ++c) {
            int u = indexar(f, c, dimension), v = f * dimension + c;
            añadirArista(u, v, 1); añadirArista(v, u, 1);
        }
    
    std::cout << 2LL * dimension * (dimension - 1) - calcularDinic() << '\n';
    return 0;
}

Etiquetas: competitive-programming c-plus-plus graph-theory number-theory data-structures

Publicado el 8-22 22:38