Medio · simulación

El círculo de José

Las reglas son cruelmente simples: cuenta k personas vivas desde la actual, la k-ésima sale del círculo, y el conteo se reanuda con la que va detrás. Sin fórmula ni astucia — solo contabilidad fiel, que es justo para lo que sirve un ordenador.

En una fraseMantén <code>alive[n]</code> y un cursor. Recorre el círculo contando vivos; al llegar al k-ésimo, escribe, márcalo muerto y reinicia el conteo en la posición siguiente (<code>cur = (cur + 1) % n</code>).

El problema

Entrada: Una línea con n y k (1 ≤ n ≤ 100, 1 ≤ k ≤ 100). Las personas van numeradas 1..n; se cuenta desde la persona 1 y la k-ésima viva es eliminada, repetidamente, hasta que no quede ninguna.

Salida: El orden de eliminación: n números separados por un espacio (el último es el superviviente).

Ejemplos:

EntradaSalida
5 33 1 5 2 4
1 71

Con n = 5, k = 3: cuenta 1, 2, 3 — la persona 3 sale. Se reanuda en 4: 4, 5, 1 — la 1 sale. Se reanuda en 2: 2, 4, 5 — la 5 sale. De nuevo desde 2 (quedan 2 y 4): 2, 4, 2 — la 2 sale; la superviviente es la 4.

Tu solución

main.cpp — tu solución
Entrada personalizada para «Ejecutar mi entrada»

La corrección corre en tu navegador contra pruebas ocultas. Un bucle infinito se corta a los 3 segundos — aun así, asegúrate de que todo bucle tenga salida. Tu código se guarda solo mientras escribes.

¿Atascado? Pistas

Pista 1 — El círculo es solo módulo

La persona i vive en el índice i−1, y la posición siguiente a n−1 es la 0: cur = (cur + 1) % n. Esa única expresión es todo el círculo. Todo problema circular que conozcas — buffers anillos, planificación round-robin — es este mismo módulo con otras ropas.

Pista 2 — Cuenta solo a los vivos

El paseo interno debe saltar las posiciones muertas sin gastar cuenta: if (alive[cur]) { count++; ... } y avanzar. Si los muertos consumieran cuentas, las eliminaciones se repetirían y el orden se autodestruiría a la tercera ronda — el ejemplo del juez lo caza al instante.

Pista 3 — El conteo se reanuda tras el caído

Tras eliminar a la persona p, el siguiente conteo empieza en la persona posterior a p — con tu cursor apuntando a p, es un cur = (cur + 1) % n más antes de la siguiente ronda. Volver a contar desde p da la misma respuesta cuando los vecinos de p viven... y diverge en silencio más tarde. Sigue el ejemplo a mano antes de confiar.

Solución (¡inténtalo primero!)
#include <iostream>
using namespace std;

bool alive[105];

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; i++) alive[i] = true;

    int cur = 0;
    for (int round = 0; round < n; round++) {
        int count = 0;
        while (true) {
            if (alive[cur]) {
                count++;
                if (count == k) break;
            }
            cur = (cur + 1) % n;
        }
        alive[cur] = false;
        cout << cur + 1;
        if (round < n - 1) cout << " ";
        cur = (cur + 1) % n;
    }
    cout << endl;
    return 0;
}

Dos ideas de contabilidad sostienen el problema entero: un array booleano como única verdad de quién queda, y el módulo como círculo. Ambas son bestias de carga — el mismo patrón alive[] marca la criba en Contar primos, y el mismo módulo mueve los buffers anillo. Cuando una historia puede rejugarse paso a paso, la simulación no es la opción ingenua: es la verificable.

Análisis profundo

Por qué funciona

A esta escala no hace falta forma cerrada — las reglas son tan simples que pueden rejugarse con fidelidad. Un array booleano alive[] es la única verdad de quién queda; el círculo es (cur + 1) % n; el conteo solo cuenta posiciones vivas. La simulación convierte el puzle en contabilidad, y la contabilidad no discute con el juez.

El coste

O(n × k) en el peor caso (cada ronda recorre hasta k posiciones), O(n) de espacio — para n = 100, k = 100 son 10.000 pasos, trivial.

Dónde falla

  • Contar posiciones muertas como vivas: las eliminaciones empiezan a repetirse y el orden se autodestruye — visible desde la tercera eliminación del ejemplo.
  • Reiniciar el conteo en la persona recién eliminada en vez de en la siguiente: coincide una ronda o dos por casualidad, y diverge en silencio después.
  • Error de uno en k: «cada k-ésima» incluye a quien empieza (con k = 1 se elimina a la persona corriente en cada ronda, dando 1 2 … n).

Siguiente paso

La contabilidad alive[] es la misma máquina que marca la criba en Contar primos; el paseo con módulo es el corazón del buffer anillo. Otro proceso fiel paso a paso que ya posees: Ordenamiento burbuja.

Dos cosas para recordar

  • Un círculo es un array más (cur + 1) % n — nada más.
  • La simulación es la herramienta honesta cuando las reglas son simples y las fórmulas no están al alcance.
🐞 Esta versión falla: ábrela y pruébala

También cuenta a los muertos: las eliminaciones se repiten y el orden se autodestruye

#include <iostream>
using namespace std;

bool alive[105];

int main() {
    int n, k;
    cin >> n >> k;
    for (int i = 0; i < n; i++) alive[i] = true;
    int cur = 0;
    for (int round = 0; round < n; round++) {
        for (int step = 0; step < k; step++) {   // bug: no alive check
            cur = (cur + 1) % n;
        }
        cur = (cur - 1 + n) % n;                 // compensate — still wrong
        alive[cur] = false;
        cout << cur + 1;
        if (round < n - 1) cout << " ";
        cur = (cur + 1) % n;
    }
    cout << endl;
    return 0;
}