Métodos y Trucos Comunes de Entrada para Programación Competitiva

En programación competitiva, la eficiencia en la entrada de datos es crucial. Aquí exploramos varias técnicas:

cin

cin es la forma más sencilla de leer datos en C++. Sin embargo, es notablemente más lenta que scanf, aunque esta diferencia se reduce significativamente si se desactiva la sincronización con la E/S estándar de C. Una ventaja de cin es su capacidad para leer diversos tipos de datos sin necesidad de epsecificar un formato explícito para cada uno, a diferencia de scanf. No obstante, puede ser menos flexible para leer formatos de entrada complejos o específicos.

cin devuelve un estado que indica el fin del archivo (EOF) cuando se encuentra.

scanf

scanf, originario de C, ofrece una velocidad de lectura considerablemente mayor que cin. Al utilizar scanf, es fundamental seleccionar el especificador de formato correcto dentro de las comillas y asegurarse de pasar las variables por referencia (usando el operador &). Una limitación importante es que scanf no puede leer directamente variables de tipo std::string.

Especificadores de Formato Comunes

  • %d: Para leer un entero de tipo int.
  • %lld: Para leer un entero de tipo long long.
  • %f: Para leer un número de punto flotante de tipo float.
  • %lf: Para leer un número de punto flotanet de tipo double.
  • %Lf: Para leer un número de punto flotante de tipo long double.
  • %c: Para leer un único carácter.
  • %s: Para leer una cadena de caracteres (a un array de char).

El uso de un especificador incorrecto puede provocar errores de ejecución.

Valor de Retorno y Entrada de Longitud Indeterminada

Al igual que cin, scanf devuelve el número de elementos leídos con éxito. Si se alcanza el fin del archivo, devuelve EOF (-1). Esta característica es útil para manejar entradas donde el número de datos no se conoce de antemano:


int count = 0;
int data[2005];
while (scanf("%d", &data[++count]) != EOF);
count--; // Ajustar el contador porque se incrementó una vez más al leer EOF

Lectura de Datos con Formato Fijo

Cuando los datos de entrada siguen un patrón específico, como una fecha con formato "hh:mm", se pueden incluir caracteres literales (como el dos puntos ':') entre los especificadores de formato en scanf. Por ejemplo, scanf("%d:%d", &hours, &minutes). Esto requiere que la entrada coincida exactamente con el formato especificado.

Ejemplo: Leer datos con formato "[ID]año/mes/día - hora:minuto:segundo":


int id, year, month, day, hour, minute, second;
scanf("[%d]%d/%d/%d - %d:%d:%d", &id, &year, &month, &day, &hour, &minute, &second);

getchar

getchar lee un solo carácter y puede ser la base para implementar rutinas de lectura rápida (fast I/O) que superan en velocidad a cin y scanf, además de ser más flexibles. Al igual que las otras funciones, devuelve EOF al final del archivo.

getchar devuelve el valor ASCII del carácter leído. Para convertir un dígito ASCII a su valor numérico, se le resta el valor ASCII de '0' (que es 48).

Implementación de Lectura Rápida

Una rutina básica de lectura rápida lee caracteres hasta encontrar un dígito, luego lee los dígitos hasta que termina el número, y finalmente convierte la secuencia de dígitos leída en un valor entero:


int fast_read() {
    int value = 0;
    int sign = 1; // 1 para positivo, -1 para negativo
    char character;

    // Ignorar caracteres no numéricos al inicio
    character = getchar();
    while (character < '0' || character > '9') {
        if (character == '-') {
            sign = -1;
        }
        character = getchar();
    }

    // Leer los dígitos del número
    while (character >= '0' && character <= '9') {
        value = value * 10 + (character - '0'); // Convertir ASCII a número
        character = getchar();
    }

    return value * sign;
}

Lectura de Caracteres Específicos

Se pueden adaptar métodos similares para leer secuencias específicas de caracteres, ignorando los demás.

Ejemplo 1: Leer una cadena binaria hasta encontrar un carácter no binario:


int n = 0;
bool binary_digits[105];
char current_char = getchar();

// Avanzar hasta el primer carácter '0' o '1'
while(current_char != '0' && current_char != '1') {
    current_char = getchar();
}

// Leer la secuencia binaria
while(current_char == '0' || current_char == '1') {
    binary_digits[++n] = (current_char == '1'); // Almacenar como booleano
    current_char = getchar();
}

Ejemplo 2: Contar espacios en un texto hasta el fin del archivo:


int space_count = 0;
char ch = getchar();
while (ch != EOF) {
    if (ch == ' ') {
        space_count++;
    }
    ch = getchar();
}

Nota sobre la entrada rápida: Si bien getchar puede ser muy rápido, métodos basados en fread suelen ser aún más eficientes para la lectura masiva de datos. Al leer desde la consola, se necesita presionar Ctrl+Z (en Windows) o Ctrl+D (en Linux/macOS) para simular el fin del archivo (EOF).

Etiquetas: scanf cin getchar fast I/O C++

Publicado el 10-19 14:50