Solución a Problemas de Programación Competitiva en C++: Algoritmos Greedy, DP y Grafos

Enviado por Chuletator online y clasificado en Informática y Telecomunicaciones

Escrito el en español con un tamaño de 9,1 KB

Recopilación de soluciones optimizadas e implementadas en C++ para diversos ejercicios clásicos de programación competitiva, abarcando técnicas como ordenamiento voraz (greedy), programación dinámica (DP), recorridos en grafos (DFS y BFS) y teoría de números.

A - Shoemaker's Problem

Enfoque: Algoritmo voraz (greedy) mediante ordenación personalizada basada en la relación costo/beneficio entre la multa y el tiempo requerido.

#include <iostream>
#include <algorithm>
using namespace std;

int tiempo[1000];
int multa[1000];
int trab[1000];

bool comp(int a, int b) {
    double res1 = (double)multa[a] / tiempo[a];
    double res2 = (double)multa[b] / tiempo[b];
    if (res1 == res2)
        return a < b;
    return res1 > res2;
}

int main() {
    int casos;
    cin >> casos;

    while (casos--) {
        int N;
        cin >> N;

        for (int i = 0; i < N; i++) {
            cin >> tiempo[i] >> multa[i];
            trab[i] = i;
        }
        sort(trab, trab + N, comp);

        for (int i = 0; i < N; i++) {
            if (i > 0) {
                cout << " ";
            }
            cout << trab[i] + 1;
        }
        cout << "\n";

        if (casos) cout << "\n";
    }
    return 0;
}

A - Cutting Sticks

Enfoque: Programación dinámica de intervalos (similar a la multiplicación encadenada de matrices) para minimizar el costo total de corte.

#include <iostream>
#include <climits>

using namespace std;

int main() {
    int l;
    while (cin >> l && l != 0) {
        int n;
        cin >> n;
        int cortes[55];
        cortes[0] = 0;

        for (int i = 1; i <= n; i++)
            cin >> cortes[i];
        cortes[n + 1] = l;
        int dp[55][55];

        for (int i = 0; i <= n + 1; i++)
            for (int j = 0; j <= n + 1; j++)
                dp[i][j] = 0;

        for (int len = 2; len <= n + 1; len++) {
            for (int i = 0; i + len <= n + 1; i++) {
                int j = i + len;
                dp[i][j] = INT_MAX;

                for (int k = i + 1; k < j; k++) {
                    int costo = dp[i][k] + dp[k][j] + (cortes[j] - cortes[i]);
                    if (costo < dp[i][j])
                        dp[i][j] = costo;
                }
            }
        }

        cout << "The minimum cutting is " << dp[0][n + 1] << "." << endl;
    }

    return 0;
}

B - The Twin Towers

Enfoque: Cálculo de la subsecuencia común más larga (LCS, Longest Common Subsequence) utilizando una tabla bidimensional de programación dinámica.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int N1, N2;
    int c = 1;
    while (true) {
        cin >> N1 >> N2;
        if (N1 == 0 && N2 == 0) {
            break;
        }

        vector<int> A(N1), B(N2);
        for (int i = 0; i < N1; i++) {
            cin >> A[i];
        }
        for (int j = 0; j < N2; j++) {
            cin >> B[j];
        }
        // dp (N1 + 1) x (N2 + 1)
        vector<vector<int>> dp(N1 + 1, vector<int>(N2 + 1, 0));
        // tabla
        for (int i = 1; i <= N1; i++) {
            for (int j = 1; j <= N2; j++) {
                if (A[i - 1] == B[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }

        cout << "Twin Towers #" << c++ << "\n";
        cout << "Number of Tiles : " << dp[N1][N2] << "\n\n";
    }
    return 0;
}

A - Vertex

Enfoque: Búsqueda en profundidad (DFS) en grafos dirigidos para identificar nodos inalcanzables a partir de un vértice dado.

#include <iostream>
#include <vector>
using namespace std;

vector<int> ady[101];
bool visitado[101];
int n;

void dfs(int u) {
    visitado[u] = true;
    for (int v : ady[u]) {
        if (!visitado[v]) dfs(v);
    }
}

int main() {
    while (true) {
        cin >> n;
        if (n == 0) break;
        for (int i = 1; i <= n; i++) {
            ady[i].clear();
        }

        while (true) {
            int a;
            cin >> a;

            if (a == 0) break;
            while (true) {
                int b;
                cin >> b;
                if (b == 0) break;
                ady[a].push_back(b);
            }
        }

        int cantidad;
        cin >> cantidad;

        while (cantidad--) {
            int inicio;
            cin >> inicio;

            for (int i = 1; i <= n; i++) visitado[i] = false;

            for (int v : ady[inicio]) {
                if (!visitado[v]) dfs(v);
            }
            vector<int> inaccesibles;
            for (int i = 1; i <= n; i++) {
                if (!visitado[i]) {
                    inaccesibles.push_back(i);
                }
            }
            cout << inaccesibles.size();
            for (int x : inaccesibles) cout << " " << x;
            cout << "\n";
        }
    }

    return 0;
}

B - Bombs! NO they are Mines!!

Enfoque: Búsqueda en anchura (BFS) en una matriz bidimensional con obstáculos para encontrar la ruta más corta.

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

int dr[4] = {1, -1, 0, 0};
int dc[4] = {0, 0, 1, -1};

int main() {
    while (true) {
        int R, C;
        cin >> R >> C;
        if (R == 0 && C == 0) break;

        vector<vector<bool> > bomba(R, vector<bool>(C, false));
        vector<vector<int> > dist(R, vector<int>(C, -1));

        int filasConBombas;
        cin >> filasConBombas;

        while (filasConBombas--) {
            int fila, cantBombas;
            cin >> fila >> cantBombas;
            for (int i = 0; i < cantBombas; i++) {
                int col;
                cin >> col;
                bomba[fila][col] = true;
            }
        }
        int sr, sc, tr, tc;
        cin >> sr >> sc >> tr >> tc;

        queue<pair<int, int> > q;
        q.push(make_pair(sr, sc));
        dist[sr][sc] = 0;
        while (!q.empty()) {
            pair<int, int> front = q.front();
            q.pop();
            int r = front.first;
            int c = front.second;
            if (r == tr && c == tc) break;
            for (int i = 0; i < 4; i++) {
                int nr = r + dr[i];
                int nc = c + dc[i];
                if (nr >= 0 && nr < R && nc >= 0 && nc < C &&
                    !bomba[nr][nc] && dist[nr][nc] == -1) {

                    dist[nr][nc] = dist[r][c] + 1;
                    q.push(make_pair(nr, nc));
                }
            }
        }
        cout << dist[tr][tc] << "\n";
    }

    return 0;
}

A - How many zero's and how many digits?

Enfoque: Teoría de números usando la fórmula de Legendre para contar factores primos en fatoriales (ceros al final) y logaritmos para determinar la cantidad de dígitos en base B.

#include <iostream>
#include <cmath>
#include <climits>
using namespace std;

int main() {
    long long N;
    int B;

    while (cin >> N >> B) {
        int base = B;
        int primo[20], exponente[20];
        int totalPrimos = 0;

        for (int p = 2; p * p <= base; p++) {
            if (base % p == 0) {
                primo[totalPrimos] = p;
                exponente[totalPrimos] = 0;
                while (base % p == 0) {
                    exponente[totalPrimos]++;
                    base /= p;
                }
                totalPrimos++;
            }
        }

        if (base > 1) {
            primo[totalPrimos] = base;
            exponente[totalPrimos] = 1;
            totalPrimos++;
        }

        long long cerosFinales = LLONG_MAX;

        for (int i = 0; i < totalPrimos; i++) {
            long long contador = 0;
            long long p = primo[i];
            for (long long x = N; x > 0; x /= p)
                contador += x / p;
            cerosFinales = min(cerosFinales, contador / exponente[i]);
        }

        if (cerosFinales == LLONG_MAX)
            cerosFinales = 0;

        long double sumaLog = 0.0;
        for (int i = 1; i <= N; i++)
            sumaLog += log((long double)i);

        long long digitos;
        if (N <= 1)
            digitos = 1;
        else
            digitos = (long long)(sumaLog / log((long double)B) + 1e-12) + 1;

        cout << cerosFinales << " " << digitos << endl;
    }

    return 0;
}

Entradas relacionadas: