Análisis y Soluciones para Desafíos de Programación

A continuación, se presentan las soluciones detalladas para varios problemas de programación competitiva, abordando diferentes enfoques algorítmicos. Estas soluciones exploran desde la simulación directa y el uso de estructuras de datos eficientes como las sumas de sufijos, hasta principios matemáticos de paridad y manipulación básica de cadenas.

Problema A: Simulación de Combate por Turnos Este problema consiste en simular una secuencia de combate entre dos oponentes, A y B. Cada oponente tiene una cantidad de salud y un valor de ataque. En cada turno, ambos se atacan simultáneamente. El objetivo es calcular el daño total acumulado durente el enfrentamiento, considerando un daño adicional si una de las partes sobrevive mientras la otra es derotada. La mecánica de la simulación es la siguiente:

  1. Inicializar un contador para el daño total infligido.
  2. Mientras al menos uno de los combatientes permanezca con vida: - Sumar el poder de ataque de ambos combatientes al daño total acumulado. - Restar el poder de ataque del oponente a la salud de cada combatiente. - Evaluar las condiciones de fin de combate: - Si la salud de ambos combatientes es cero o menor, el combate finaliza. - Si solo un combatiente ha caído (salud ≤ 0), el combatiente superviviente inflige un daño adicional equivalente a 10 veces su propio poder de ataque, y el combate termina.
  3. La simulación se detiene en cuanto se cumple cualquiera de las condiciones de fin de combate.
#include <iostream> // Para entrada/salida estándar

int main() {
    std::ios_base::sync_with_stdio(false); // Optimización de E/S
    std::cin.tie(NULL);                   // Desactiva la sincronización con C stdio

    long long poder_ataque_A, salud_inicial_A;
    long long poder_ataque_B, salud_inicial_B;
    std::cin >> poder_ataque_A >> salud_inicial_A >> poder_ataque_B >> salud_inicial_B;

    long long dano_total_generado = 0;

    while (salud_inicial_A > 0 || salud_inicial_B > 0) {
        dano_total_generado += (poder_ataque_A + poder_ataque_B); // Suma el daño de ambos en el turno

        salud_inicial_A -= poder_ataque_B; // A recibe daño de B
        salud_inicial_B -= poder_ataque_A; // B recibe daño de A

        if (salud_inicial_A <= 0 && salud_inicial_B <= 0) {
            // Ambos combatientes caen simultáneamente o ya estaban inactivos
            break;
        } else if (salud_inicial_A <= 0) {
            // Solo A cae. B sobrevive y realiza un ataque final
            dano_total_generado += poder_ataque_B * 10;
            break;
        } else if (salud_inicial_B <= 0) {
            // Solo B cae. A sobrevive y realiza un ataque final
            dano_total_generado += poder_ataque_A * 10;
            break;
        }
    }
    std::cout << dano_total_generado << std::endl; // Imprime el daño total
    return 0;
}


Problema D: Búsqueda de Subarreglos Óptimos con Sumas de Sufijos El desafío en este problema es identificar un subarreglo contiguo que cumpla con criterios específicos de optimización: se busca maximizar un valor de "satisfacción" y, en caso de empate, minimizar un valor de "vergüenza". En caso de un segundo empate, se prefiere el subarreglo con el índice inicial más bajo. Para manejar eficientemente las sumas de subarreglos, se emplea la técnica de sumas de sufijos. La estrategia de solución se degslosa en los siguientes puntos:

  1. Se leen dos secuencias de valores: una para la satisfacción y otra para la vergüenza, junto con un parámetro entero K.
  2. Se construyen arreglos de sumas de sufijos para ambas secuencias. Por ejemplo, sufijo_satis[i] contendrá la suma de los valores de satisfacción desde el índice i hasta el final del arreglo. Esto permite calcular la suma de cualquier subarreglo [inicio, fin] como sufijo_satis[inicio] - sufijo_satis[fin + 1].
  3. Se generan todos los subarreglos candidatos que se deben evaluar. Estos incluyen: - Subarreglos de longitud L (donde 1 ≤ L ≤ K) que terminan en la última posición del arreglo (índice N). - Subarreglos de longitud exacta K que comienzan en cualquier posición desde 1 hasta N-K.
  4. Cada subarreglo candidato se almacena junto con su suma total de satisfacción, su suma total de vergüenza y su índice de inicio.
  5. Finalmente, se ordena la lista de candidatos utilizando una función de comparación personalizada que aplica las reglas de optimización: primero por mayor satisfacción (descendente), luego por menor vergüenza (ascendente), y por último por el menor índice de inicio (ascendente).
  6. El índice de inicio del primer candidato en la lista ordenada es la respuesta deseada.
#include <iostream>
#include <vector>
#include <algorithm> // Para std::sort

const int MAX_ELEMENTOS = 100005;

struct ValoresSegmento {
    long long satis, verguenza;
};

struct OpcionSubarreglo {
    long long total_satis, total_verguenza;
    int indice_inicio;
};

// Arreglo para almacenar los valores originales de cada elemento
ValoresSegmento elementos_originales[MAX_ELEMENTOS];
// Arreglo para almacenar las sumas de sufijos
OpcionSubarreglo sumas_acumuladas_sufijo[MAX_ELEMENTOS]; 

// Función de comparación para ordenar los candidatos
bool compararOpciones(const OpcionSubarreglo& op1, const OpcionSubarreglo& op2) {
    if (op1.total_satis != op2.total_satis) {
        return op1.total_satis > op2.total_satis; // Mayor satisfacción primero
    }
    if (op1.total_verguenza != op2.total_verguenza) {
        return op1.total_verguenza < op2.total_verguenza; // Menor vergüenza segundo
    }
    return op1.indice_inicio < op2.indice_inicio; // Menor índice de inicio tercero
}

int main() {
    std::ios_base::sync_with_stdio(false); // Optimización de E/S
    std::cin.tie(NULL);                   // Desactiva la sincronización con C stdio

    int N_total, K_longitud;
    std::cin >> N_total >> K_longitud;

    for (int i = 1; i <= N_total; ++i) std::cin >> elementos_originales[i].satis;
    for (int i = 1; i <= N_total; ++i) std::cin >> elementos_originales[i].verguenza;

    // Calcular las sumas de sufijos para satisfacción y vergüenza
    sumas_acumuladas_sufijo[N_total + 1] = {0, 0}; // Elemento centinela para simplificar cálculos
    for (int i = N_total; i >= 1; --i) {
        sumas_acumuladas_sufijo[i].total_satis = sumas_acumuladas_sufijo[i + 1].total_satis + elementos_originales[i].satis;
        sumas_acumuladas_sufijo[i].total_verguenza = sumas_acumuladas_sufijo[i + 1].total_verguenza + elementos_originales[i].verguenza;
    }

    std::vector<OpcionSubarreglo> opciones_validas;

    // 1. Generar subarreglos de longitud L (1 <= L <= K_longitud) que terminan en N_total
    for (int i = 0; i < K_longitud; ++i) {
        int inicio_segmento = N_total - i;
        if (inicio_segmento >= 1) { // Asegurar que el índice de inicio sea válido
            opciones_validas.push_back({
                sumas_acumuladas_sufijo[inicio_segmento].total_satis - sumas_acumuladas_sufijo[N_total + 1].total_satis,
                sumas_acumuladas_sufijo[inicio_segmento].total_verguenza - sumas_acumuladas_sufijo[N_total + 1].total_verguenza,
                inicio_segmento
            });
        } else {
            break; // No hay más segmentos válidos que terminan en N_total
        }
    }

    // 2. Generar subarreglos de longitud exacta K_longitud que comienzan desde 1 hasta N_total - K_longitud
    if (N_total >= K_longitud) { // Solo si K_longitud es una longitud posible para subarreglos
        for (int i = 1; i <= N_total - K_longitud; ++i) { // 'i' es el índice de inicio
            opciones_validas.push_back({
                sumas_acumuladas_sufijo[i].total_satis - sumas_acumuladas_sufijo[i + K_longitud].total_satis,
                sumas_acumuladas_sufijo[i].total_verguenza - sumas_acumuladas_sufijo[i + K_longitud].total_verguenza,
                i
            });
        }
    }
    
    // Si N_total < K_longitud, el primer bucle cubrirá todos los segmentos de longitud 1 a N_total.
    // El segundo bucle no se ejecutará, lo cual es correcto en ese escenario.

    std::sort(opciones_validas.begin(), opciones_validas.end(), compararOpciones);

    std::cout << opciones_validas[0].indice_inicio << std::endl;

    return 0;
}

Problema F: Juego de Paridad en un Tablero Cuadrado Este problema presenta un juego sobre un tablero de N x M celdas donde dos jugadores se turnan para colorear celdas. Aunque se menciona una restricción sobre no colorear celdas adyacentes del mismo color, en un juego de este tipo con estrategia óptima, la restricción suele no ser determinante si siempre hay celdas disponibles para ambos jugadores. La clave del resultado se reduce a la paridad del número total de celdas en el tablero. Asumiendo que los jugadores siempre pueden hacer un movimiento válido (es decir, colorear una celda que no viole las reglas o que las viola de manera óptima para forzar una situación favorable), el juego es esencialmente un conteo de turnos:

  • Si el número total de celdas (N * M) es impar, el primer jugador ("akai") siempre tendrá la oportunidad de realizar el último movimiento, garantizando su victoria.
  • Si el número total de celdas (N * M) es par, el segundo jugador ("yukari") será quien realice el último movimiento, asegurando la victoria. #include // Para entrada/salida estándar #include // Para std::string

int main() { std::ios_base::sync_with_stdio(false); // Optimización de E/S std::cin.tie(NULL); // Desactiva la sincronización con C stdio

long long numero_filas, numero_columnas; std::cin >> numero_filas >> numero_columnas;

long long total_cuadrados = numero_filas * numero_columnas;

if (total_cuadrados % 2 != 0) { // Si el total de celdas es impar std::cout << "akai" << std::endl; } else { // Si el total de celdas es par std::cout << "yukari" << std::endl; }

return 0; }


<h3><strong>Problema J: Procesamiento Simple de Cadenas</strong></h3>
<p>El problema J es una tarea directa de manipulación de cadenas de caracteres. Requiere leer una línea completa de texto de la entrada estándar y luego imprimir esa misma línea, pero excluyendo los últimos tres caracteres. Es crucial usar una función que pueda leer cadenas que contengan espacios hasta el final de la línea.</p>

#include // Para entrada/salida estándar #include // Para std::string y sus métodos

int main() { std::ios_base::sync_with_stdio(false); // Optimización de E/S std::cin.tie(NULL); // Desactiva la sincronización con C stdio

std::string texto_completo; std::getline(std::cin, texto_completo); // Leer la línea completa, incluyendo espacios

// Verificar que la cadena tenga al menos 3 caracteres para evitar un error de índice if (texto_completo.length() >= 3) { // Imprimir la subcadena desde el inicio hasta (longitud_total - 3) caracteres std::cout << texto_completo.substr(0, texto_completo.length() - 3) << std::endl; } else { // Si la cadena tiene menos de 3 caracteres, se imprime una cadena vacía. // O se podrían imprimir los caracteres restantes si el problema lo exigiera. std::cout << "" << std::endl; }

return 0; }


Etiquetas: C++ algoritmos Simulación Sumas de Sufijos Ordenación

Publicado el 6-4 16:34