Análisis de soluciones: AtCoder Beginner Contest 382

A continuación, presento un desglose técnico de los problemas abordados durante el AtCoder Beginner Contest 382, enfocándome en la lógica algorítmica y la optimización.

Problema C: Estrategia de Selección

Dado que los elemantos de mayor valor son consumidos por los primeros individuos de la secuencia, la capacidad efectiva de los participantes resulta en una sublista monótona decreciente. Para resolver esto de manera eficiente, filtramos los candidatos válidos y aplicamos una búsqueda binaria, logrando una complejidad de O(M log N).


// Estructura simplificada de la lógica de búsqueda
void resolver() {
   int n, m;
   std::cin >> n >> m;
   std::vector<int> a(n), b(m);
   // ... lectura de datos ...
   
   std::vector<std::pair<int, int>> min_indices;
   int actual_min = 2e9;
   for(int i = 0; i < n; ++i) {
       if(a[i] < actual_min) {
           actual_min = a[i];
           min_indices.push_back({actual_min, i + 1});
       }
   }
   
   for(int i = 0; i < m; ++i) {
       auto it = std::upper_bound(min_indices.begin(), min_indices.end(), 
                 std::make_pair(b[i], 2000000000));
       if(it == min_indices.begin()) std::cout << -1 << "\n";
       else std::cout << std::prev(it)->second << "\n";
   }
}
   

Problema D: Generación de Secuencias con Backtracking

Para construir las secuencias bajo la restrición ai ≥ ai-1 + 10, implementamos una búsqueda en profundidad (DFS). Aplicamos una poda esencial: si la suma actual más el incremento mínimo requerido excede el límite superior M, terminamos la rama prematuramente.


void buscar(int idx, int ultimo_valor, std::vector<int>& seq, int N, int M) {
   if (idx == N) {
       // Almacenar o imprimir secuencia completa
       return;
   }
   for (int v = ultimo_valor + 10; v + 10 * (N - 1 - idx) <= M; ++v) {
       seq.push_back(v);
       buscar(idx + 1, v, seq, N, M);
       seq.pop_back();
   }
}
   

Problema E: Esperanza Matemática y Programación Dinámica

Para calcular la esperanza del número de intentos necesarios para obtener X cartas raras, definimos P(i, j) como la probabilidad de obtener exactamente j cartas raras al abrir un paquete. El problema se reduce a resolver la recurrancia: E[i] = (Σ E[i-j] * P(n, j) + 1) / (1 - P(n, 0)). La implementación requiere un enfoque de DP con complejidad O(N * M).


void resolver_esperanza() {
   // DP para probabilidades de obtener 'j' cartas raras
   std::vector<double> dp_prob(n + 1, 0.0);
   dp_prob[0] = 1.0;
   for (double p : probs) {
       for (int j = n; j >= 1; --j)
           dp_prob[j] = dp_prob[j] * (1.0 - p) + dp_prob[j - 1] * p;
       dp_prob[0] *= (1.0 - p);
   }
   
   // Cálculo de esperanza
   std::vector<double> E(m + 1, 0.0);
   for (int i = 1; i <= m; ++i) {
       for (int j = 1; j <= std::min(i, n); ++j)
           E[i] += E[i - j] * dp_prob[j];
       E[i] = (E[i] + 1.0) / (1.0 - dp_prob[0]);
   }
}
   

Etiquetas: cpp algoritmos programacion-dinamica búsqueda-binaria DFS

Publicado el 6-9 00:23