com
Contenido
Prefacio vi
Prefacio viii
lista de abreviaciones XX
1 Introducción 1
1.1 Programación Competitiva. . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Consejos para ser Competitivo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Sugerencia 1: ¡Escriba el código más rápido! . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Sugerencia 2: Identifique rápidamente los tipos de problemas . . . . . . . . . . . . . . . . . 4
1.2.3 Sugerencia 3: Realice un análisis de algoritmos. . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Consejo 4: Lenguajes de programación maestros . . . . . . . . . . . . . . . . . 10
1.2.5 Sugerencia 5: Domine el arte de probar el código . . . . . . . . . . . . . . . . . 13
1.2.6 Consejo 6: Práctica y más práctica . . . . . . . . . . . . . . . . . . . 15
1.2.7 Consejo 7: Trabajo en equipo (para ICPC) . . . . . . . . . . . . . . . . . . . . . . dieciséis
i
CONTENIDO ©
c Steven y Félix
4 Grafico 121
4.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
4.2 Gráfico transversal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.1 Primera búsqueda en profundidad (DFS) . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.2 Búsqueda primero en amplitud (BFS) . . . . . . . . . . . . . . . . . . . . . . . 123
4.2.3 Encontrar componentes conectados (gráfico no dirigido) . . . . . . . . . 125
4.2.4 Relleno por inundación: etiquetado/coloreado de los componentes conectados. . . . . . 125
4.2.5 Clasificación topológica (Gráfico acíclico dirigido) . . . . . . . . . . . . . . . 126
4.2.6 Comprobación de gráfico bipartito. . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.2.7 Comprobación de la propiedad de los bordes del gráfico a través del árbol de expansión DFS. . . . . . . . . 128
4.2.8 Búsqueda de puntos de articulación y puentes (gráfico no dirigido) . . . . . 130
4.2.9 Encontrar componentes fuertemente conectados (Gráfico dirigido) . . . . . . 133
4.3 Árbol de expansión mínimo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.2 Algoritmo de Kruskal. . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.3 Algoritmo de Prim. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.3.4 Otras aplicaciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.4 Rutas más cortas de fuente única. . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.4.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . 146
4.4.2 SSSP en gráfico no ponderado. . . . . . . . . . . . . . . . . . . . . . . 146
4.4.3 SSSP en gráfico ponderado. . . . . . . . . . . . . . . . . . . . . . . . 148
4.4.4 SSSP en gráfico con ciclo de peso negativo. . . . . . . . . . . . . . 151
4.5 Rutas más cortas de todos los pares. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
4.5.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . 155
4.5.2 Explicación de la solución DP de Floyd Warshall. . . . . . . . . . . . . 156
4.5.3 Otras aplicaciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.6 Flujo de red. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.2 Método de Ford Fulkerson. . . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.3 Algoritmo de Edmonds Karp. . . . . . . . . . . . . . . . . . . . . . . 164
4.6.4 Modelado de gráfico de flujo - Parte 1 . . . . . . . . . . . . . . . . . . . . . . 166
4.6.5 Otras aplicaciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
4.6.6 Modelado de gráfico de flujo - Parte 2 . . . . . . . . . . . . . . . . . . . . . . 168
yo
CONTENIDO ©
c Steven y Félix
5 Matemáticas 191
5.1 Descripción general y motivación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
5.2 Problemas matemáticos ad hoc. . . . . . . . . . . . . . . . . . . . . . . . . 192
5.3 Clase BigInteger de Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
5.3.1 Funciones básicas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
5.3.2 Funciones de bonificación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
5.4 Combinatoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.4.1 Números de Fibonacci. . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.4.2 Coeficientes binomiales. . . . . . . . . . . . . . . . . . . . . . . . . . . 205
5.4.3 Números catalanes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
5.4.4 Comentarios sobre Combinatoria en Concursos de Programación. . . . . . . 206
5.5 Teoría de números. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.5.1 Números primos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.5.2 Máximo común divisor y mínimo común múltiplo. . . . . . . . . 211
5.5.3 Factoriales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
5.5.4 Búsqueda de factores primos con divisiones de prueba optimizadas. . . . . . . . . 212
5.5.5 Trabajar con factores primos. . . . . . . . . . . . . . . . . . . . . . . 213
5.5.6 Funciones que involucran factores primos. . . . . . . . . . . . . . . . . . . 214
5.5.7 Tamiz modificado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
5.5.8 Módulo aritmético. . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
5.5.9 Euclides ampliado: resolución de la ecuación diofántica lineal . . . . . . . . 217
5.5.10 Comentarios sobre Teoría de Números en Concursos de Programación. . . . . . 217
5.6 Teoría de la probabilidad. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
5.7 Búsqueda de ciclos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
5.7.1 Solución(es) utilizando una estructura de datos eficiente. . . . . . . . . . . . . . . 223
5.7.2 Algoritmo de búsqueda de ciclos de Floyd. . . . . . . . . . . . . . . . . . . . 223
5.8 Teoría de juegos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.8.1 Árbol de decisión. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.8.2 Conocimientos matemáticos para acelerar la solución. . . . . . . . . . . . 227
5.8.3 Juego Nim. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
5.9 Solución de ejercicios sin asterisco. . . . . . . . . . . . . . . . . . . . . . . . 229
5.10 Notas del capítulo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
iii
CONTENIDO ©
c Steven y Félix
IV
CONTENIDO ©
c Steven y Félix
A uCaza 393
B Créditos 396
Bibliografía 398
v
CONTENIDO ©
c Steven y Félix
Prefacio
Hace mucho tiempo (el 11elde noviembre de 2003, martes, 3:55:57 UTC), recibí un correo
electrónico con el siguiente mensaje:
“Debo decir en pocas palabras que con el Sitio UVa, ha dado a luz a una nueva
CIVILIZACIÓN y con los libros que escribe (se refería a “Programming Challenges:
The Programming Contest Training Manual” [60], en coautoría con Steven Skiena) ,
inspiras a los soldados a seguir marchando. Que vivas mucho tiempo para servir a la
humanidad produciendo programadores sobrehumanos”.
Aunque eso era claramente una exageración, me hizo pensar. Tenía un sueño: crear una comunidad
en torno al proyecto que había iniciado como parte de mi trabajo docente en la UVa, con personas de
todo el mundo trabajando juntas por un mismo ideal. Con un poco de búsqueda, encontré
rápidamente toda una comunidad en línea que ejecuta un anillo web de sitios con excelentes
herramientas que cubren y brindan todo lo que le faltaba al sitio UVa.
Para mí, 'Methods to Solve' de Steven Halim, un estudiante muy joven de Indonesia, fue uno de
los sitios web más impresionantes. Me inspiré a creer que el sueño se haría realidad algún día, porque
en este sitio web yacía el resultado del arduo trabajo de un genio de los algoritmos y la informática.
Además, sus objetivos declarados coincidían con el núcleo de mi sueño: servir a la humanidad. Aún
mejor, tiene un hermano con intereses y capacidades similares, Felix Halim.
Es una pena que se tarde tanto en empezar una colaboración real, pero la vida es así.
Afortunadamente, todos hemos seguido trabajando juntos de manera paralela hacia la
realización de ese sueño, el libro que ahora tienes en tus manos es prueba de ello.
No imagino mejor complemento para el Juez Online de la UVa. Este libro utiliza muchos ejemplos de
UVa cuidadosamente seleccionados y categorizados tanto por tipo de problema como por técnica de
resolución, proporcionando una ayuda increíblemente útil para los usuarios del sitio. Al dominar y practicar
la mayoría de los ejercicios de programación de este libro, un lector puede resolver fácilmente al menos 500
problemas en el Juez en línea de UVa, que los colocará entre los 400 y 500 primeros entre ≈100000 usuarios
UVa DO.
Está claro que el libro “Programación Competitiva: Incrementando el Límite Inferior de los
Concursos de Programación” es adecuado para los programadores que quieren mejorar sus
posiciones en los próximos ICPC regionales e IOI. Los dos autores han pasado por estos
concursos (ICPC e IOI) como concursantes y ahora como entrenadores. Pero también es un
colega esencial para los recién llegados, como dicen Steven y Felix en la introducción, "el libro no
debe leerse una vez, sino varias veces".
Además, contiene código fuente C++ práctico para implementar algoritmos dados. Comprender
un problema es una cosa, pero conocer el algoritmo para resolverlo es otra, e implementar bien la
solución en un código breve y eficiente es complicado. Después de haber leído este extraordinario
libro tres veces, te darás cuenta de que eres mucho mejor programador y, lo que es más importante,
una persona más feliz.
vi
CONTENIDO ©
c Steven y Félix
viii
CONTENIDO ©
c Steven y Félix
Prefacio
Este libro es imprescindible para todo programador competitivo. Dominar el contenido de
este libro es una condición necesaria (pero tal vez no suficiente) si uno desea dar un salto y
pasar de ser un codificador cualquiera a estar entre los mejores programadores del mundo.
3. Entrenadores que buscan materiales de formación integrales para sus alumnos [24],
requisitos previos
Este libro esnoescrito para programadores novatos. Este libro está dirigido a lectores que tengan al menos
conocimientos básicos en metodología de programación, estén familiarizados con al menos uno de estos
lenguajes de programación (C/C++ o Java, preferiblemente ambos), hayan aprobado un curso básico de
estructuras de datos y algoritmos (normalmente impartido en primer año del plan de estudios universitario
de Ciencias de la Computación), y comprender el análisis algorítmico simple (al menos la notación O
grande). En la tercera edición, se ha agregado más contenido para que este libro también pueda usarse
como lectura complementariapara un básicoEstructuras de datos y algoritmoscurso.
viii
CONTENIDO ©
c Steven y Félix
Sabemos que uno probablemente no puede ganar el ACM ICPC regional simplemente dominando los
contenidos delversión actual (tercera edición)de este libro Si bien hemos incluido una gran cantidad de
materiales en este libro, mucho más que en las dos primeras ediciones, somos conscientes de que se
requiere mucho más de lo que este libro puede ofrecer para lograr esa hazaña. En las notas de los capítulos
se enumeran algunos indicadores adicionales de referencias útiles para los lectores que desean más.
Creemos, sin embargo, que a su equipo le irá mucho mejor en futuros CIPC después de dominar el
contenido de este libro. Esperamos que este libro le sirva tanto de inspiración como de motivación para su
viaje de 3 a 4 años compitiendo en ACM ICPC durante sus días universitarios.
Muchos de nuestros consejos para los concursantes de ACM ICPC también se aplican a usted. Los planes de
estudio de ACM ICPC e IOI son muy similares, excepto que IOI,por ahora, actualmente excluye los temas
enumerados en la siguiente Tabla 1. Puede omitir estos elementos hasta sus años universitarios (cuando se una a
los equipos ACM ICPC de esa universidad). Sin embargo, aprender estas técnicas por adelantado definitivamente
puede ser beneficioso ya que algunas tareas en IOI pueden volverse más fáciles con conocimiento adicional.
Sabemos que uno no puede ganar una medalla en IOI simplemente dominando los contenidos del versión
actual (tercera edición)de este libro Si bien creemos que muchas partes del plan de estudios de IOI se han incluido
en este libro, con la esperanza de permitirle lograr una puntuación respetable en futuros IOI, somos muy
conscientes de que las tareas modernas de IOI requieren habilidades agudas para resolver problemas y una
creatividad tremenda, virtudes que no podemos. posiblemente impartir a través de este libro de texto estático.
Este libro puede brindarle conocimientos, pero el trabajo duro en última instancia lo debe hacer usted. Con la
práctica viene la experiencia, y con la experiencia viene la habilidad. Entonces, ¡sigue practicando!
ix
CONTENIDO ©
c Steven y Félix
Este libro se utiliza en el curso CS3233 - 'Programación competitiva' de Steven en la Escuela de Informática de la
Universidad Nacional de Singapur. CS3233 se lleva a cabo en 13 semanas de enseñanza utilizando el siguiente plan
de lección (consulte la Tabla 2). Las diapositivas en PDF (solo la versión pública) se encuentran en el sitio web
complementario de este libro. Los compañeros maestros/entrenadores deben sentirse libres de modificar el plan
de la lección para adaptarlo a las necesidades de los estudiantes. Sugerencias o soluciones breves de lassin
estrella Los ejercicios escritos de este libro se encuentran al final de cada capítulo. Algunos de lossembrado de
estrellas Los ejercicios escritos son bastante desafiantes y no tienen pistas ni soluciones. Estos probablemente se
pueden usar como preguntas de examen o problemas de concurso (por supuesto, ¡resuélvalos primero!).
Este libro también se utiliza como lectura complementaria en el curso CS2010 de Steven:
'Estructuras de datos y algoritmos', principalmente para la implementación de varios algoritmos y
ejercicios escritos/de programación.
X
CONTENIDO ©
c Steven y Félix
Partes del Capítulo 4 también se pueden usar como lectura complementaria oimplementaciónguía para
mejorar unMatemáticas discretas[57, 15] o un básicoAlgoritmoscurso. También proporcionamos algunos
conocimientos nuevos sobre la visualización de técnicas de programación dinámica como algoritmos en DAG.
Actualmente, tal discusión es lamentablemente poco común en muchos libros de texto de Ciencias de la
Computación.
Debido a su diversidad de cobertura y profundidad de discusión, este libro esnodestinado a ser leído
una vez, pero varias veces. Hay muchos escritos (≈238) y ejercicios de programación (≈1675)
enumerados y distribuidos en casi todas las secciones. Puede omitir estos ejercicios al principio si la
solución es demasiado difícil o requiere mayor conocimiento y técnica, y repasarlos después de
estudiar otros capítulos de este libro. Resolver estos ejercicios fortalecerá su comprensión de los
conceptos que se enseñan en este libro, ya que generalmente involucran aplicaciones, giros o
variantes interesantes del tema que se está discutiendo. Haga un esfuerzo para intentarlos: el tiempo
que dedique a resolver estos problemas definitivamente no se desperdiciará.
Creemos que este libro es y será relevante para muchos estudiantes universitarios y de
secundaria. Los concursos de programación como el ICPC y el IOI han llegado para quedarse, al
menos durante muchos años. Los nuevos estudiantes deben tratar de comprender e interiorizar los
conocimientos básicos presentados en este libro antes de buscar nuevos desafíos. Sin embargo, el
término 'básico' puede ser un poco engañoso; consulte la tabla de contenido para comprender lo que
queremos decir con 'básico'.
Como puede implicar el título de este libro, el propósito de este libro es claro: nuestro objetivo es
mejorar las habilidades de programación de todos y, por lo tanto, aumentar lalímite inferiorde concursos de
programación como el ICPC y el IOI en el futuro. Con más concursantes dominando los contenidos de este
libro, esperamos que el año 2010 (cuando se publicó la primera edición de este libro) sea un punto de
inflexión que marque una mejora acelerada en los estándares de los concursos de programación.
Esperamos ayudar a más equipos a resolver más (≥2) problemas en futuros ICPC y ayudar a más
concursantes a lograr mayores (≥200) puntuaciones en futuros IOI. También esperamos ver a muchos
entrenadores del ICPC y del IOI de todo el mundo (especialmente en el Sudeste Asiático) adoptar este libro
por la ayuda que proporciona para dominar temas de los que los estudiantes no pueden prescindir en
concursos de programación competitivos. Si se logra tal proliferación del conocimiento de "límite inferior"
requerido para la programación competitiva, entonces se habrá cumplido el objetivo principal de este libro
de aumentar el nivel del conocimiento humano, y nosotros, como autores de este libro, estaremos muy
felices. Por supuesto.
Convención
Hay mucho código C/C++ y también algo de código Java (especialmente en la Sección 5.3)
incluidos en este libro. Si aparecen, se escribirán enesta fuente monoespaciada.
Para el código C/C++ de este libro, hemos adoptado el uso frecuente dedefinición de tipoCorreo
electrónico y macros: funciones que los programadores de la competencia suelen utilizar por conveniencia,
brevedad y velocidad de codificación. Sin embargo, no podemos usar técnicas similares para Java ya que no
contiene características similares o análogas. Estos son algunos ejemplos de nuestros atajos de código C/C+
+:
xi
CONTENIDO ©
c Steven y Félix
Categorización de problemas
A partir del 24 de mayo de 2013, Steven y Felix, combinados, han resuelto problemas de 1903 UVa (≈46.45% de
todo el conjunto de problemas de UVa). Sobre≈1675 de ellos se discuten y clasifican en este libro. Desde finales de
2011, algunos problemas de Live Archive también se han integrado en UVa Online Judge. En este libro, usamos
ambas cosasnumeraciones de problemas, pero la clave de clasificación principal utilizada en la sección de índice de
este libro es el número de problema UVa.
Estos problemas se clasifican según un'balanceo de carga'esquema: Si un problema se puede
clasificar en dos o más categorías, se ubicará en la categoría con menor número de problemas. De
esta forma, puede encontrar que algunos problemas han sido categorizados 'erróneamente', donde
la categoría en la que aparece puede no coincidir con la técnica que ha utilizado para resolverlo. Solo
podemos garantizar que si ve el problema X en la categoría Y, entonces sabe quenosotroshan logrado
resolver el problema X con la técnica mencionada en la sección que trata sobre la categoría Y.
xi
CONTENIDO ©
c Steven y Félix
• El primer cambio notable es el diseño. Ahora tenemos una mayor densidad de información en cada página.
El 2Dakota del NorteLa edición utiliza interlineado sencillo en lugar del interlineado de 1,5 utilizado en la edición
1.S tedición. También se mejora el posicionamiento de las figuras pequeñas para que tengamos un diseño
más compacto. Esto es para evitar aumentar demasiado el número de páginas y al mismo tiempo agregar
más contenido.
• Se han solucionado algunos errores menores en nuestros ejemplos de código (tanto los que se muestran en el
libro como las copias electrónicas proporcionadas en el sitio web complementario). Todos los ejemplos de código
ahora tienen comentarios mucho más significativos para ayudar en la comprensión.
1. Muchos nuevos problemas Ad Hoc para iniciar este libro (Sección 1.4).
2. Un conjunto ligero de técnicas booleanas (manipulación de bits) (Sección 2.2), Gráficos
implícitos (Sección 2.4.1) y estructuras de datos Fenwick Tree (Sección 2.4.4).
3. Más DP: Una explicación más clara de DP de abajo hacia arriba, elO(norteIniciar sesiónk) solución para
el problema LIS, la suma de mochila/subconjunto 0-1 y DP TSP (utilizando la técnica de máscara de
bits) (Sección 3.5.2).
4. Una reorganización del material del gráfico en: Graph Traversal (tanto DFS como BFS),
árbol de expansión mínimo, rutas más cortas (fuente única y todos los pares), flujo
máximo y gráficos especiales. Los nuevos temas incluyen el algoritmo MST de Prim, una
discusión de DP como un recorrido en DAG implícitos (Sección 4.7.1), Gráficos Eulerianos
(Sección 4.7.3) y el algoritmo de ruta de aumento (Sección 4.7.4).
5. Una reorganización de las técnicas matemáticas (Capítulo 5) en: Ad Hoc, Java
BigInteger, Combinatorics, Number Theory, Probability Theory, Cycle-Finding, Game
Theory (nuevo) y Powers of a (Square) Matrix (nuevo). Cada tema ha sido reescrito
para mayor claridad.
6. Habilidades básicas de procesamiento de cadenas (Sección 6.2), más problemas relacionados con cadenas (Sección
6.3), incluida la coincidencia de cadenas (Sección 6.4) y una explicación mejorada del árbol/
matriz de sufijos (Sección 6.6).
7. Más bibliotecas de geometría (Capítulo 7), especialmente sobre puntos, líneas y polígonos.
8. Un nuevo capítulo 8, que contiene una discusión sobre descomposición de problemas, técnicas de
búsqueda avanzada (A*, búsqueda limitada en profundidad, profundización iterativa, IDA*), técnicas
avanzadas de DP (más técnicas de máscara de bits, el problema del cartero chino, una compilación
de DP comunes estados, una discusión sobre mejores estados de DP y algunos problemas de DP más
difíciles).
XIII
CONTENIDO ©
c Steven y Félix
• Muchas figuras existentes en este libro han sido redibujadas y mejoradas. Se han agregado muchas figuras
nuevas para ayudar a explicar los conceptos con mayor claridad.
• La primera edición está escrita principalmente desde el punto de vista del concursante del ICPC y del
programador de C++. La segunda edición está escrita para ser más equilibrada e incluye la perspectiva del
IIO. La compatibilidad con Java también se ha mejorado considerablemente en la segunda edición. Sin
embargo, no admitimos ningún otro lenguaje de programación hasta el momento.
• El sitio web 'Métodos para resolver' de Steven ahora se ha integrado completamente en este libro en forma
de 'sugerencias de una sola línea' para cada problema y el útil índice de problemas en la parte posterior de
este libro. Ahora, llegar a los 1000 problemas resueltos en UVa online Judge ya no es un sueño
descabellado (creemos que esta hazaña es factible con unserioLicenciatura universitaria de CS de 4 años).
• ≈600 ejercicios de programación más de UVa Online Judge y Live Archive han sido resueltos por
Steven & Felix y agregados a este libro. También hemos agregado muchos más ejercicios
escritos a lo largo del libro con sugerencias/soluciones breves como apéndices.
• La tercera edición ahora usa un tamaño de fuente ligeramente más grande (12 pt) en comparación con la segunda
edición (11 pt), un aumento del 9 por ciento. Con suerte, muchos lectores encontrarán el texto más legible esta
vez. También usamos figuras más grandes. Estas decisiones, sin embargo, han aumentado el número de páginas y
han hecho que el libro sea más grueso. También hemos ajustado el margen izquierdo/derecho en las páginas
pares/impares para aumentar la legibilidad.
• El diseño se ha cambiado para comenzar casi todas las secciones en una página nueva. Esto es para hacer que el
diseño sea mucho más fácil de administrar.
• hemos añadidomucho masejercicios escritos a lo largo del libro y los clasificó ensin estrella(
con fines de autocomprobación; consejos/soluciones están al final de cada capítulo) y
protagonizado *versiones (para desafíos adicionales; no se proporciona ninguna solución). Los
ejercicios escritos se han colocado cerca de la discusión relevante en el cuerpo del texto.
• ≈Steven & Felix han resuelto 477 ejercicios de programación más de UVa Online Judge y Live Archive y, en
consecuencia, se han agregado a este libro. Por lo tanto, hemos mantenido un importante≈50% (para ser
precisos,≈46.Cobertura del 45 % de los problemas del juez en línea UVa incluso cuando el juez ha crecido en
el mismo período de tiempo. Estos nuevos problemas se han enumerado en unfuente cursiva. Algunos de
los problemas más nuevos han reemplazado a los más antiguos a medida queDeberías intentar
problemas. Todos los ejercicios de programación ahora se colocan siempre al final de una sección.
xiv
CONTENIDO ©
c Steven y Félix
1. El Capítulo 1 contiene una introducción más suave para los lectores que son nuevos en la
programación competitiva. Hemos desarrollado formatos de entrada/salida (E/S) más estrictos
en problemas típicos de programación y rutinas comunes para tratar con ellos.
2. Agregamos una estructura de datos lineal más: 'deque' en la Sección 2.2. El Capítulo 2 ahora contiene
una discusión más detallada de casi todas las estructuras de datos discutidas en este capítulo,
especialmente las Secciones 2.3 y 2.4.
3. En el Capítulo 3, tenemos una discusión más detallada de varias técnicas de búsqueda completa:
bucles anidados, generación de subconjuntos/permutaciones iterativamente y retroceso
recursivo. Nuevo: un truco interesante para escribir e imprimir soluciones de DP de arriba hacia
abajo, discusión del algoritmo de Kadane para Max 1D Range Sum.
5. Capítulo 5: Hemos incluido una mayor cobertura de problemas matemáticos Ad Hoc, una
discusión de una interesante operación Java BigInteger:es probableprimo, Se agregaron/
ampliaron varias fórmulas de combinatoria de uso común y se modificaron algoritmos de
tamiz, se ampliaron/revisaron secciones sobre Teoría de la probabilidad (Sección 5.6),
Búsqueda de ciclos (Sección 5.7) y Teoría de juegos (Sección 5.8).
6. Capítulo 6: Reescribimos la Sección 6.6 para tener una mejor explicación de Suffix Trie/
Tree/ Array reintroduciendo el concepto de carácter terminal.
7. Capítulo 7: Recortamos este capítulo en dos secciones principales y mejoramos la calidad del código de la
biblioteca.
8. Capítulo 8: Los temas más difíciles que se enumeraron en el Capítulo 1-7 en el 2Dakota del NorteLa edición
ahora se ha reubicado en el Capítulo 8 (o el Capítulo 9 a continuación). Nuevo: Discusión de la rutina
de retroceso más difícil, búsqueda de espacio de estado, encuentro en el medio, truco de usar BST
balanceado como tabla de notas y una sección más completa sobre la descomposición del problema.
9. Nuevo Capítulo 9: Se han agregado varios temas raros que aparecen de vez en cuando en los
concursos de programación. Algunos de ellos son fáciles, pero muchos de ellos son difíciles y pueden
ser determinantes de puntaje algo importantes en los concursos de programación.
XV
CONTENIDO ©
c Steven y Félix
• mi encantadora esposa, Grace Suryani, por permitirme dedicar nuestro valioso tiempo a este
proyecto.
• mi hermano menor y coautor, Felix Halim, por compartir muchas estructuras de datos,
algoritmos y trucos de programación para mejorar la escritura de este libro.
• mi padre Lin Tjie Fong y mi madre Tan Hoey Lan por criarnos y animarnos a hacer
bien nuestro estudio y trabajo.
• mi amiga Ilham Winata Kurnia por la lectura de prueba del manuscrito de la primera edición.
• compañeros Asistentes de enseñanza de CS3233 y ACM ICPC Trainers @ NUS: Su Zhan, Ngo
Minh Duc, Melvin Zhang Zhiyong, Bramandia Ramadhana.
• mis estudiantes de CS3233 en Sem2 AY2008/2009 que me inspiraron para crear las notas
de clase y estudiantes en Sem2 AY2009/2010 que verificaron el contenido de la primera
edición de este libro y dieron la contribución inicial de Live Archive
xvi
CONTENIDO ©
c Steven y Félix
• los correctores de pruebas: Siete de los estudiantes CS3233 arriba (subrayados) más Tay Wenbin.
• Por último, pero no menos importante, quiero volver a agradecer a mi esposa, Grace Suryani, por dejarme hacer
otra ronda del tedioso proceso de edición de libros mientras estaba embarazada de nuestro primer bebé: Jane
Angelina Halim.
xvii
CONTENIDO ©
c Steven y Félix
• compañero Asistente de enseñanza de CS3233 @ NUS en los últimos dos años: Harta
Wijaya, Trinh Tuan Phuong y Huang Da.
• los lectores de prueba: Seis de los estudiantes CS3233 en Sem2 AY2011/2012 (subrayado) y
Hubert Teo Hua Kian.
• mi esposa Grace Suryani y mi hija Jane Angelina por su amor en nuestra familia.
Derechos de autor
Ninguna parte de este libro puede ser reproducida o transmitida de ninguna forma o por ningún medio, ya sea
electrónica o mecánicamente, incluidas las fotocopias, el escaneo, la carga en cualquier sistema de
almacenamiento y recuperación de información.
xviii
CONTENIDO ©
c Steven y Félix
1Tesis doctoral: "Un enfoque integrado de caja blanca + negra para diseñar y ajustar algoritmos de búsqueda
xix
CONTENIDO ©
c Steven y Félix
XX
Lista de tablas
xxx
Lista de Figuras
1.4 Algunas referencias que inspiraron a los autores a escribir este libro. . . . . . . . . 31
3.1 8-Reinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3,2 UVa 10360 [47] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.3 Mi antepasado (se ordenan las 5 rutas de la raíz a la hoja) . . . . . . . . . . . . . . . . 85
3.4 Visualización de UVa 410 - Balance de la Estación. . . . . . . . . . . . . . . . . . . 90
3.5 UVa 410 - Observaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.6 UVa 410 - Solución codiciosa. . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.7 UVa 10382 - Hierba de riego. . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.8 PD ascendente (no se muestran las columnas 21 a 200) . . . . . . . . . . . . . . 100
3.9 Subsecuencia creciente más larga . . . . . . . . . . . . . . . . . . . . . . . . . 106
3.10 Cambio de moneda. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
3.11 Un gráfico completo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.12 Ilustración de cortar palos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
XXII
LISTA DE FIGURAS ©
c Steven y Félix
7.1 Punto giratorio (10, 3) 180 grados en sentido antihorario alrededor del origen (0, 0) 272
7.2 Distancia a la Línea (izquierda) y al Segmento de Línea (centro); Producto cruzado (derecha) 274
XXIII
LISTA DE FIGURAS ©
c Steven y Félix
XXIV
Capítulo 1
Introducción
Una ilustración: UVa Online Judge [47] Problema número 10911 (Formación de equipos de prueba).
Sean (x, y) las coordenadas de la casa de un estudiante en un plano 2D. Hay 2norte
estudiantes y queremosparellos ennortegrupos Dejardiser el dista∑nce entre las casas
de 2 alumnos en grupoi. Formanortegrupos tales quecosto= i=1diesminimizado.
norte
1Algunas competencias de programación se realizan en equipo para fomentar el trabajo en equipo, ya que los ingenieros de software
generalmente no trabajan solos en la vida real.
2Al ocultar los datos de prueba reales del enunciado del problema, la programación competitiva alienta a los
solucionadores de problemas a ejercitar su fuerza mental para pensar en todos los casos posibles del problema y probar
sus programas con esos casos. Esto es típico en la vida real, donde los ingenieros de software tienen que probar mucho su
software para asegurarse de que cumple con los requisitos establecidos por los clientes.
1
1.1. PROGRAMACIÓN COMPETITIVA ©
c Steven y Félix
Ahora pregúntate: ¿Cuál de los siguientes te describe mejor? Tenga en cuenta que si no tiene
claro el material o la terminología que se muestra en este capítulo, puede volver a leerlo
después de leer este libro una vez.
• Programador no competitivo A (también conocido como el borroso):
Paso 1: Lee el problema y se confunde. (Este problema es nuevo para él). Paso
2: intenta codificar algo: lee la entrada y la salida no triviales.
Paso 3: Se da cuenta de que todos sus intentos sonnoAceptado (CA):
Codicioso(Sección 3.4): Emparejar repetidamente a los dos estudiantes restantes con las
distancias de separación más cortas da laRespuesta incorrecta (WA).
IngenuoBúsqueda completa: Usando el retroceso recursivo (Sección 3.2) y probando todos los
rendimientos de emparejamiento posiblesLímite de tiempo excedido (TLE).
• Programador competitivo D:
Completa todos los pasos tomados por el programador no competitivo C en≤30 minutos.
Tenga en cuenta que estar bien versado en la programación competitiva esnoel objetivo final, sino sólo un
medio para un fin. El verdadero objetivo final es producir científicos/programadores informáticos completos
que estén mucho más preparados para producir un mejor software y enfrentar problemas de investigación
de informática más difíciles en el futuro. Los fundadores del ACM International Collegiate Programming
Contest (ICPC) [66] tienen esta visión y nosotros, los autores, estamos de acuerdo con ella. Con este libro,
jugamos nuestro pequeño papel en la preparación de las generaciones actuales y futuras para que sean
más competitivos en el tratamiento de los conocidos problemas de CS planteados con frecuencia en los
recientes ICPC y la Olimpiada Internacional de Informática (IOI).
2
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
Ejercicio 1.1.1: La estrategia codiciosa del programador no competitivo A anterior realmente funciona para
el caso de prueba de muestra que se muestra en la Figura 1.1. por favor da unmejorcontraejemplo!
Ejercicio 1.1.2: Analice la complejidad temporal de la ingenua solución de búsqueda completa del
programador no competitivo A anterior para comprender por qué recibe el veredicto de TLE.
Ejercicio 1.1.3*: En realidad, una solución inteligente de retroceso recursivocon podatodavía puede resolver
este problema. ¡Resuelva este problema sin usar una tabla DP!
¡En serio! Aunque este consejo puede no significar mucho ya que ICPC y (especialmente) IOI no son
concursos de mecanografía, hemos visto Rankiy rangoi+1 equipos ICPC separados solo por unos minutos y
concursantes frustrados de IOI que se pierden de salvar puntos importantes al no poder codificar una
solución de fuerza bruta de último minuto correctamente. Cuando pueda resolver la misma cantidad de
problemas que su competidor, se deberá a la habilidad de codificación (su capacidad para producir código
conciso y sólido) y... la velocidad de escritura.
Pruebe esta prueba de mecanografía enhttp://www.typingtest.comy siga las instrucciones allí sobre cómo
mejorar su habilidad de escritura. el de steven es∼85-95 palabras por minuto y Felix es∼55-65 palabras por minuto.
Si su velocidad de escritura es mucho menor que estos números, ¡tome este consejo en serio!
Además de poder escribir caracteres alfanuméricos de forma rápida y correcta, también
deberá familiarizar sus dedos con las posiciones de los caracteres del lenguaje de programación
de uso frecuente: paréntesis () o{}o corchetes [] o corchetes angulares<>, el punto y coma ; y dos
puntos:, comillas simples '' para caracteres, comillas dobles "" para cadenas, el ampersand &, la
barra vertical o la 'tubería'|, el signo de exclamación !, etc.
Como un poco de práctica, intente escribir el código fuente de C++ a continuación lo más rápido posible.
3Si percibe que el material presentado en este libro es de dificultad intermedia o avanzada depende de
su habilidad de programación antes de leer este libro.
3
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
return memo[máscara de bits] = ans; // almacena el resultado en una tabla de notas y regresa
}
int principal() {
int i, j, caseNo = 1, x[20], y[20]; //
freopen("10911.txt", "r", stdin); // redirigir archivo de entrada a stdin
En los ICPC, los concursantes (equipos) reciben unestablecerde problemas (≈7-12 problemas) de diferentes tipos.
A partir de nuestra observación de conjuntos de problemas recientes de la región de Asia del CIPC, podemos
categorizar los tipos de problemas y su tasa de aparición como en la Tabla 1.1.
4
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
En los IOI, los concursantes reciben 6 tareas en 2 días (8 tareas en 2 días en 2009-2010) que cubren los puntos 1-5
y 10, con unmucho más pequeñasubconjunto de elementos 6-10 en la Tabla 1.1. Para obtener más información,
consulte el plan de estudios del IOI de 2009 [20] y la clasificación de problemas del IOI de 1989-2008 [67].
La clasificación en la Tabla 1.1 está adaptada de [48] y de ninguna manera completa. Algunas técnicas,
por ejemplo, 'clasificación', no se clasifican aquí porque son 'triviales' y generalmente se usan solo
como una 'subrutina' en un problema mayor. No incluimos 'recursividad' ya que está incrustada en
categorías como retroceso recursivo o programación dinámica. También omitimos las 'estructuras de
datos' ya que el uso de una estructura de datos eficiente puede considerarse integral para resolver
problemas más difíciles. Por supuesto, los problemas a veces requieren técnicas mixtas: un problema
se puede clasificar en más de un tipo. Por ejemplo, el algoritmo de Floyd Warshall es a la vez una
solución para el problema gráfico de All-Pairs Shortest Paths (APSP, Sección 4.5) y un algoritmo de
Programación Dinámica (DP) (Sección 3.5). Los algoritmos de Prim y Kruskal son soluciones para el
árbol de expansión mínimo (MST, Sección 4. 3) problema gráfico y algoritmos Greedy (Sección 3.4). En
la Sección 8.4, discutiremos problemas (más difíciles) que requieren más de un algoritmo y/o
estructuras de datos para ser resueltos.
En el futuro (cercano), estas clasificaciones pueden cambiar. Un ejemplo significativo es la programación
dinámica. Esta técnica no se conocía antes de la década de 1940, ni se usaba con frecuencia en ICPC o IOI antes de
mediados de la década de 1990, pero hoy en día se considera un requisito previo definitivo. A modo de ilustración:
Había≥3 problemas de PD (de 11) en la reciente final mundial del ICPC 2010.
Sin embargo, el objetivo principal esnosimplemente asociar problemas con las técnicas requeridas para
resolverlos como en la Tabla 1.1. Una vez que esté familiarizado con la mayoría de los temas de este libro,
también podrá clasificar los problemas en los tres tipos de la tabla 1.2.
Ser - estarcompetitivo, eso es,hacer bienen un concurso de programación, debe ser capaz de clasificar
problemas con confianza y frecuencia como tipo A y minimizar la cantidad de problemas que clasifica en
tipo B. Es decir, necesita adquirir suficiente conocimiento de algoritmos y desarrollar sus habilidades de
programación para que pueda considerar muchos problemas clásicos para ser fácil. Sin embargo, avictoria
un concurso de programación, también necesitarás desarrollar habilidadeshabilidades para resolver
problemas (por ejemplo, reducir el problema dado a un problema conocido, identificar pistas sutiles o
5
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
propiedades en el problema, atacando el problema desde un ángulo no obvio, etc.) para que usted (o
su equipo) pueda derivar la solución requerida a un problema duro/original de tipo C en IOI o ICPC
Regionales/Finales mundiales y hacer asi quedentro dela duración del concurso.
Ejercicio 1.2.1: Lea los problemas UVa [47] que se muestran en la Tabla 1.3 y determine sus tipos de
problemas. Dos de ellos han sido identificados para usted. Llenar esta tabla es fácil después de dominar
este libro; todas las técnicas necesarias para resolver estos problemas se analizan en este libro.
Una vez que haya diseñado un algoritmo para resolver un problema particular en un concurso de programación,
debe hacerse esta pregunta: dado el límite de entrada máximo (generalmente dado en una buena descripción del
problema), ¿puede el algoritmo desarrollado actualmente, con su complejidad de tiempo/espacio , pasar el límite
de tiempo/memoria dado para ese problema en particular?
A veces, hay más de una forma de atacar un problema. Algunos enfoques pueden ser incorrectos, otros no lo
suficientemente rápidos y otros 'exagerados'. Una buena estrategia es hacer una lluvia de ideas para muchos
algoritmos posibles y luego elegir ella solución más simple que funciona(es decir, es lo suficientemente rápido
como para pasar el límite de tiempo y memoria y aun así producir la respuesta correcta)4!
Las computadoras modernas son bastante rápidas y pueden procesar5hasta≈100METRO(o 108; 1METRO=
1,000,000) operaciones en pocos segundos. Puede usar esta información para determinar si su algoritmo se
ejecutará a tiempo. Por ejemplo, si el tamaño de entrada máximonortees 100k(o 105; 1k=1,000), y su
algoritmo actual tiene una complejidad de tiempo deO(norte2), el sentido común (o su calculadora) le
informará que (100k)2o 1010es un número muy grande que indica que su algoritmo requerirá (del orden de)
cientos de segundos para ejecutarse. Por lo tanto, deberá diseñar un algoritmo más rápido (y también
correcto) para resolver el problema. Supongamos que encuentra uno que se ejecuta con una complejidad de
tiempo deO(norteIniciar sesión2norte). Ahora, su calculadora le informará que 105Iniciar sesión2105
es solo 1.7×106y el sentido común dicta que el algoritmo (que ahora debería ejecutarse en menos de un
segundo) probablemente podrá pasar el límite de tiempo.
4Discusión: es cierto que en los concursos de programación, elegir el algoritmo más simple que funcione es crucial para obtener buenos
resultados en ese concurso de programación. Sin embargo, durantesesiones de entrenamiento, donde las limitaciones de tiempo no son un
problema, puede ser beneficioso dedicar más tiempo a tratar de resolver un determinado problema utilizando elmejor algoritmo posible.
Estamos mejor preparados de esta manera. Si nos encontramos con una versión más difícil del problema en el futuro, ¡tendremos más
posibilidades de obtener e implementar la solución correcta!
5Trata esto como una regla general. Estos números pueden variar de una máquina a otra.
6
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
Los límites del problema son tan importantes como la complejidad temporal de su algoritmo para
determinar si su solución es adecuada. Suponga que solo puede diseñar un algoritmo relativamente simple
de codificar que se ejecuta con una complejidad de tiempo horrenda deO(norte4). Esto puede parecer una
solución inviable, pero sinorte≤50, entonces realmente has resuelto el problema. Puedes implementar tuO(
norte4) algoritmo con impunidad desde 504son solo 6.25METROy su algoritmo aún debería ejecutarse en
alrededor de un segundo.
Tenga en cuenta, sin embargo, que el orden de complejidad no indica necesariamente el número real
de operaciones que requerirá su algoritmo. Si cada iteración involucra una gran cantidad de operaciones
(muchos cálculos de coma flotante o una cantidad significativa de subbucles constantes), o si su
implementación tiene una 'constante' alta en su ejecución (bucles repetidos innecesariamente o pases
múltiples, o incluso si /O o sobrecarga de ejecución), su código puede tardar más en ejecutarse de lo
esperado. Sin embargo, este no suele ser el caso, ya que los autores del problema deberían haber diseñado
los límites de tiempo para que un algoritmo bien codificado con una complejidad de tiempo adecuada logre
un veredicto AC.
Al analizar la complejidad de su algoritmo con el límite de entrada dado y el límite de
tiempo/memoria establecido, puede decidir mejor si debe intentar implementar su
algoritmo (lo que llevará un tiempo precioso en los ICPC e IOI), intentar mejorar su
algoritmo primero, o cambiar a otros problemas en el conjunto de problemas.
Como se mencionó en el prefacio de este libro, vamos anodiscutir el concepto de análisis
algorítmico en detalles. Nosotrosasumirque ya tienes esta habilidad básica. Hay una multitud de
otros libros de referencia (por ejemplo, "Introducción a los algoritmos" [7], "Diseño de
algoritmos" [38], "Algoritmos" [8], etc.) que lo ayudarán a comprender los siguientes conceptos
previos/ técnicas en análisis algorítmico:
– Realice el análisis amortizado (por ejemplo, consulte el Capítulo 17 de [7]), aunque rara vez se usa en
concursos, para minimizar la posibilidad de obtener el veredicto 'Límite de tiempo excedido', o peor
aún, considerando que su algoritmo es demasiado lento y omite el problema cuando de hecho, es lo
suficientemente rápido en el sentido amortizado.
– Realice un análisis sensible a la salida para analizar el algoritmo que (también) depende del tamaño
de la salida y minimice la posibilidad de obtener el veredicto 'Límite de tiempo excedido'. Por
ejemplo, un algoritmo para buscar una cadena con longitudmetroen una cadena larga con la ayuda
de un árbol de sufijos (que ya está construido) se ejecuta enO(metro+occ) tiempo. El tiempo que
tarda este algoritmo en ejecutarse no solo depende del tamaño de entradametrosino también el
tamaño de salida: el número de ocurrenciasocc(ver más detalles en la Sección 6.6).
7
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
– Se pueden usar enteros sin signo si solo se requieren números no negativos. enteros
sin signo de 32 bits (int sin firmar)y enteros sin signo de 64 bits (largo largo sin
firmar)tienen límites superiores de 232−1≈4×109y 264−1≈1.8×1019respectivamente.
– Si necesita almacenar números enteros≥264, utilice la técnica Big Integer (Sección 5.3).
– Existennorte! permutaciones y 2nortesubconjuntos (o combinaciones) denorteelementos.
– Normalmente,O(norteIniciar sesión2norte) los algoritmos son suficientes para resolver la mayoría de los problemas de concursos.
– El tamaño de entrada más grande para problemas típicos de concursos de programación debe ser<1
METRO. Más allá de eso, el tiempo necesario para leer la entrada (la rutina de Entrada/Salida) será el
cuello de botella.
– Una CPU típica del año 2013 puede procesar 100METRO=108operaciones en pocos segundos.
Muchos programadores novatos se saltarían esta fase e inmediatamente comenzarían a implementar el primer
algoritmo (ingenuo) que se les ocurriera solo para darse cuenta de que la estructura de datos o el algoritmo
elegido no es lo suficientemente eficiente (o incorrecto). Nuestro consejo para los concursantes del ICPC6:
Absténgase de codificar hasta que esté seguro de que su algoritmo es correcto y lo suficientemente rápido.
≤10k norte) O(norte2) por ejemplo, Burbuja/Selección/Ordenación por inserción (Sección 2.2)
≤1METRO O(norteIniciar sesión2norte) p. ej. Merge Sort, construyendo Segment Tree (Sección 2.3) La mayoría de los
≤100METRO O(norte), oh(Iniciar sesión2norte), oh(1) problemas de concursos tienennorte≤1METRO(cuello de botella de E/S)
Tabla 1.4: Complejidades de tiempo de la regla empírica para el 'peor algoritmo de CA' para varios tamaños de
entrada de caso úniconorte, asumiendo que su CPU puede calcular 100METROartículos en 3s.
Para ayudarlo a comprender el crecimiento de varias complejidades de tiempo comunes y, por lo tanto, ayudarlo a
juzgar qué tan rápido es 'suficiente', consulte la Tabla 1.4. También se encuentran variantes de tales tablas en
muchos otros libros sobre estructuras de datos y algoritmos. Esta tabla está escrita a partir de unperspectiva del
concursante de programación. Por lo general, las restricciones de tamaño de entrada se dan en una (buena)
descripción del problema. Suponiendo que una CPU típica puede ejecutar cien millones de operaciones en
alrededor de 3 segundos (el límite de tiempo típico en la mayoría de los problemas de UVa [47]), podemos predecir
el 'peor' algoritmo que aún puede pasar el límite de tiempo. Por lo general, el algoritmo más simple tiene la menor
complejidad de tiempo, pero si puede pasar el límite de tiempo, ¡utilícelo!
6A diferencia de ICPC, las tareas de IOI generalmente se pueden resolver (parcial o totalmente) utilizando varias soluciones posibles, cada
una con diferentes complejidades de tiempo y puntajes de subtareas. Para ganar puntos valiosos, puede ser bueno usar una solución de
fuerza bruta para anotar algunos puntos y comprender mejor el problema. No habrá una penalización de tiempo significativa ya que IOI no
es una competencia de velocidad. Luego, mejore iterativamente la solución para ganar más puntos.
8
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
En la Tabla 1.4, vemos la importancia de usar buenos algoritmos con pequeños órdenes de crecimiento, ya que nos
permiten resolver problemas con tamaños de entrada más grandes. Pero un algoritmo más rápido generalmente no es
trivial y, a veces, es sustancialmente más difícil de implementar. En la Sección 3.2.3, discutimos algunos consejos que
pueden permitir el uso de la misma clase de algoritmos con tamaños de entrada más grandes. En capítulos posteriores,
también explicamos algoritmos eficientes para varios problemas de computación.
Ejercicio 1.2.2: responda las siguientes preguntas utilizando su conocimiento actual sobre los
algoritmos clásicos y sus complejidades temporales. Una vez que haya terminado de leer este libro
una vez, puede ser beneficioso intentar este ejercicio nuevamente.
1. Haynortepáginas web (1≤norte≤10METRO). Cada página webitiene un rango de páginari. Desea elegir
las 10 páginas principales con los rangos de página más altos. ¿Qué método es mejor?
(a) Cargar todonorteel rango de página de las páginas web en la memoria, ordénelas (Sección 2.2) en orden descendente
de rango de página, obteniendo los 10 primeros.
(b) Use una estructura de datos de cola de prioridad (un montón) (Sección 2.3).
(a) Pruebe todas las submatrices posibles y compruebe si la media de cada submatriz es 7. Este
algoritmo se ejecuta enO(METRO3× norte3).
(b) Pruebe todas las submatrices posibles, pero enO(METRO2×norte2) con esta técnica: .
5. Debe calcular el camino más corto entre dos vértices en un gráfico acíclico dirigido (DAG)
ponderado con|V |, |E|≤100k. ¿Qué algoritmo(s) se pueden usar en un concurso de
programación típico (es decir, con un límite de tiempo de aproximadamente 3 segundos)?
9
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
6. ¿Qué algoritmo produce una lista de los primeros 10knúmeros primos con la mejor complejidad
de tiempo? (Sección 5.5.1)
9. Desea enumerar todas las ocurrencias de una subcadenaPAGS(de longitudmetro) en una cadena (larga) T(de
longitudnorte), Si alguna. Ambas cosasnorteymetrotener un máximo de 1 millón de caracteres.
7Opinión personal: De acuerdo con las últimas reglas de la competencia IOI 2012, Java todavía no es compatible
con IOI. Los lenguajes de programación permitidos en IOI son C, C++ y Pascal. Por otro lado, las Finales Mundiales
del ICPC (y por lo tanto la mayoría de las Regionales) permiten el uso de C, C++ y Java en el concurso. Por lo tanto,
parece que el 'mejor' lenguaje para dominar es C++, ya que es compatible con ambas competencias y tiene un
fuerte soporte STL. Si los concursantes de IOI eligen dominar C++, tendrán la ventaja de poder usar el mismo
lenguaje (con un mayor nivel de dominio) para ACM ICPC en sus actividades de nivel universitario.
10
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
cuando falla (a diferencia de los volcados del núcleo o las fallas de segmentación en C/C++). Por otro lado, C/
C++ también tiene sus propios méritos. Dependiendo del problema en cuestión, cualquiera de los idiomas
puede ser la mejor opción para implementar una solución en el menor tiempo posible.
¡Suponga que un problema requiere que calcule 25! (el factorial de 25). La respuesta es muy grande:
15.511.210.043.330.985.984.000.000. Esto supera con creces el tipo de datos entero primitivo incorporado
más grande (sin firmar largo largo:264−1). Como todavía no hay una biblioteca aritmética de precisión
arbitraria integrada en C/C++, habríamos tenido que implementar una desde cero. El código Java, sin
embargo, es extremadamente simple (más detalles en la Sección 5.3). En este caso, el uso de Java
definitivamente reduce el tiempo de codificación.
importar java.util.Scanner;
importar java.math.BigInteger;
3
0.1227...
0.517611738...
0.7341231223444344389923899277...
# incluir <cstdio>
utilizando el espacio de nombres estándar;
int N; // usar variables globales en concursos puede ser una buena estrategia //
charx[110]; acostúmbrese a establecer el tamaño de la matriz un poco más grande de lo necesario
int principal() {
escaneo("%d\n", &N);
mientras (N--) { // simplemente hacemos un bucle desde N, N-1, N-2, ..., 0 //
scanf("0.%[0-9]...\n", &x); '&' es opcional cuando x es una matriz de caracteres
// nota: si te sorprende el truco anterior, // comprueba los
detalles de scanf en www.cppreference.com
printf("los digitos son 0.%s\n", x); } } //
devuelve 0;
11
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
No muchos programadores de C/C++ conocen las capacidades parciales de expresión regular integradas en
la biblioteca de E/S estándar de C. A pesar de queescanear/imprimirson rutinas de E/S de estilo C, todavía se
pueden usar en código C++. Muchos programadores de C++ se 'obligan' a usarcin / couttodo el tiempo
aunque a veces no es tan flexible comoescanear/imprimiry también es mucho más lento.
En los concursos de programación, especialmente en los ICPC, el tiempo de codificación debenoser el
principal cuello de botella. Una vez que descubra el 'peor algoritmo de CA' que pasará el límite de tiempo
dado, ¡se espera que pueda traducirlo en un código sin errores y rápido!
¡Ahora, pruebe algunos de los ejercicios a continuación! Si necesita más de 10 líneas de código para resolver
cualquiera de ellos, ¡debe volver a visitar y actualizar su conocimiento de su(s) lenguaje(s) de programación! El
dominio de los lenguajes de programación que utiliza y sus rutinas incorporadas es extremadamente importante y
lo ayudará mucho en los concursos de programación.
Ejercicio 1.2.3: Producir código de trabajo que eslo más conciso posiblepara las siguientes tareas:
2. Dado un número enteronorte(norte≤15), imprimirπanortedígitos después del punto decimal (redondeado). (por
ejemplo, paranorte=2, imprimir3.14;pornorte=4, imprimir3.1416;pornorte=5, impresiones3.14159.)
3. Dada una fecha, determine el día de la semana (lunes, . . . , domingo) de ese día. (por ejemplo, el 9 de
agosto de 2010, la fecha de lanzamiento de la primera edición de este libro, es un lunes).
5. Dadas las distintas y válidas fechas de nacimiento denortepersonas como triples (DD, MM, AAAA), ordénelas primero
por meses de nacimiento ascendentes (MM), luego por fechas de nacimiento ascendentes (DD) y finalmente por
edad ascendente.
7. Genere todas las permutaciones posibles de{'A B C', . . . , 'J'}, el primeronorte=10 letras
del alfabeto (ver Sección 3.2.1).
8. Genere todos los subconjuntos posibles de{0, 1, 2, . . . ,norte-1}, pornorte=20 (ver Sección 3.2.1).
9. Dada una cadena que representa un número en base X, conviértala en una cadena
equivalente en base Y, 2≤X, Y≤36. Por ejemplo: “FF” en base X = 16 (hexadecimal) es “255”
en baseY1=10 (decimal) y “11111111” en baseY2=2 (binario). Consulte la Sección 5.3.2.
10. Definamos una 'palabra especial' como un alfabeto en minúsculas seguido de dos dígitos consecutivos. Dada una
cadena, reemplace todas las 'palabras especiales' de longitud 3 con 3 estrellas "***", por ejemplo
S = “línea: a70 y z72 serán reemplazadas, aa24 y a872 no”
debe transformarse en:
S = “línea: *** y *** serán reemplazados, aa24 y a872 no”.
11. Dado unválidoexpresión matemática que incluye '+', '-', '*', '/', '(' y ')' en una sola línea,
evalúe esa expresión. (por ejemplo, una expresión bastante complicada pero válida3 + (8 -
7.5) * 10 / 5 - (2 + 5 * 7)debe producir -33.0cuando se evalúa con precedencia de
operadores estándar).
12
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
1. Sus casos de prueba deben incluir los casos de prueba de muestra, ya que se garantiza que la salida de
muestra sea correcta. Usar 'FC'en Windows o 'diferencia'en UNIX para verificar la salida de su código
(cuando se le da la entrada de muestra) contra la salida de muestra. Evite la comparación manual, ya que
los humanos son propensos a cometer errores y no son buenos para realizar tales tareas, especialmente
para problemas con formatos de salida estrictos (por ejemplo, línea en blancoEntrecasos de prueba versus
después de cadaCasos de prueba). Para hacer esto,copiar y pegarla entrada de muestra y la salida de
muestra de la descripción del problema, luego guárdelas en archivos (llamados como 'aporte'y 'producción'
o cualquier otra cosa que sea sensata). Luego, después de compilar su programa (supongamos que el
nombre del ejecutable es 'g++'defecto 'a.fuera'),ejecútelo con una redirección de E/S: './a.out < entrada >
misalida'.Finalmente, ejecuta 'diff mi salida de salida'para resaltar cualquier diferencia (potencialmente
sutil), si existe alguna.
2. Para problemas con múltiples casos de prueba en una sola ejecución (consulte la Sección 1.3.2),
debe incluir dos casos de prueba de muestra idénticos consecutivamente en la misma
ejecución. Ambos deben generar las mismas respuestas correctas conocidas. Esto ayuda a
determinar si olvidó inicializar alguna variable; si la primera instancia produce la respuesta
correcta pero la segunda no, es probable que no haya restablecido sus variables.
3. Sus casos de prueba deben incluir casos de esquina complicados. Piense como el autor del problema e
intente encontrar la peor entrada posible para su algoritmo mediante la identificación de casos.
8Los entornos de los concursos de programación difieren de un concurso a otro. Esto puede poner en desventaja a los
concursantes que confían demasiado en el elegante entorno de desarrollo integrado (IDE) (por ejemplo, Visual Studio, Eclipse, etc.)
para la depuración. Puede ser una buena idea practicar la codificación con solo uneditor de textoy uncompilador!
13
1.2. CONSEJOS PARA SER COMPETITIVO ©
c Steven y Félix
que están "ocultos" o implícitos en la descripción del problema. Estos casos generalmente se incluyen en
los casos de prueba secretos del juez, peronoen la entrada y salida de la muestra. Los casos de esquina
típicamente ocurren en valores extremos tales comonorte=0,norte=1, valores negativos, valores finales (y/o
intermedios) grandes que no se ajustan a enteros con signo de 32 bits, etc.
4. Sus casos de prueba deben incluirlargocasos. Aumente el tamaño de entrada gradualmente hasta los
límites de entrada máximos establecidos en la descripción del problema. Use casos de prueba
grandes con estructuras triviales que sean fáciles de verificar con cálculo manual y grandesaleatorio
casos de prueba para probar si su código termina a tiempo y aún produce un resultado razonable (ya
que sería difícil verificar la corrección aquí). A veces, su programa puede funcionar para casos de
prueba pequeños, pero produce una respuesta incorrecta, falla o excede el límite de tiempo cuando
aumenta el tamaño de entrada. Si eso sucede, verifique si hay desbordamientos, errores fuera de
límite o mejore su algoritmo.
5. Aunque esto es raro en los concursos de programación modernos, no asuma que la entrada siempre
estará bien formateada si la descripción del problema no lo establece explícitamente (especialmente
para un problema mal escrito). Intente insertar espacios en blanco adicionales (espacios,
tabulaciones) en la entrada y pruebe si su código aún puede obtener los valores correctamente sin
fallar.
Sin embargo, después de seguir cuidadosamente todos estos pasos, aún puede obtener veredictos
que no sean de AC. En ICPC, usted (y su equipo) pueden considerar el veredicto del juez y la tabla de
líderes (generalmente disponible durante las primeras cuatro horas del concurso) para determinar su
próximo curso de acción. En IOI 2010-2012, los concursantes tienen un número limitado de tokens
para verificar la corrección de su código presentado contra los casos de prueba secretos. Con más
experiencia en tales concursos, podrá hacer mejores juicios y elecciones.
(d) (En concursos por equipos): Pida a su compañero de equipo que rehaga el problema.
4. Otro seguimiento de la Pregunta 2: ¿Qué pasa si el máximonortees 1.000, la salida solo depende del
tamaño de la entradanorte, y todavía tienescuatro horasde tiempo de competencia restante?
14
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
6. Treinta minutos después del concurso, echas un vistazo a la tabla de líderes. Existen
muchos otros equipos que han resuelto un problemaXque su equipo no ha intentado.
¿Qué debes hacer?
7. A la mitad del concurso, echas un vistazo a la tabla de líderes. El equipo líder (suponga
que no es su equipo) acaba de resolver el problemaY.¿Qué debes hacer?
8. Su equipo ha dedicado dos horas a un problema desagradable. Has enviado varias implementaciones
por parte de diferentes miembros del equipo. Todas las presentaciones han sido juzgadas
incorrectas. No tienes idea de lo que está mal. ¿Qué debes hacer?
9. Queda una hora para el final del concurso. Tienes 1 código WA y 1 idea nueva para
otroproblema. ¿Qué debe hacer usted (o su equipo)?
(a) Abandone el problema con el código WA, cambie al otro problema en un intento
de resolver un problema más.
(b) Insista en que tiene que depurar el código WA. No hay suficiente tiempo para empezar a trabajar
en un nuevo problema.
(c) (En ICPC): Imprima el código WA. Pida a otros dos miembros del equipo que lo analicen mientras
usted cambia a ese otro problema en un intento de resolverlo.dosmas problemas.
Figura 1.2: Izquierda: Juez en línea de la Universidad de Valladolid; Derecha: ACM ICPC Live Archive.
15
1.3. PRIMEROS PASOS: LOS PROBLEMAS FÁCILES ©
c Steven y Félix
La Olimpiada de Computación de EE. UU. tiene un sitio web de capacitación muy útil [48] con concursos en línea
para ayudarlo a aprender habilidades de programación y resolución de problemas. Esto está más orientado a los
participantes del IIO que a los participantes del CIPC. Ve directo a su sitio web y entrena.
Sphere Online Judge [61] es otro juez en línea donde los usuarios calificados pueden agregar sus
problemas. Este juez en línea es bastante popular en países como Polonia, Brasil y Vietnam. Hemos utilizado
este SPOJ para publicar algunos de nuestros problemas de autoría propia.
Figura 1.3: Izquierda: USACO Training Gateway; Derecha: Juez en línea de Esfera
TopCoder organiza frecuentes 'Single Round Match' (SRM) [32] que consisten en algunos problemas que se resolverán en
1-2 horas. Después del concurso, se le da la oportunidad de 'desafiar' el código de otros concursantes proporcionando
casos de prueba complicados. Este juez en línea utiliza un sistema de calificación (codificadores rojos, amarillos, azules,
etc.) para recompensar a los concursantes que son realmente buenos en la resolución de problemas con una calificación
más alta en comparación con los concursantes más diligentes que resuelven una mayor cantidad de problemas más
fáciles.
Este último consejo no es algo fácil de enseñar, pero aquí hay algunas ideas que pueden valer la pena
probar para mejorar el rendimiento de su equipo:
• Practique la codificación en papel en blanco. (Esto es útil cuando su compañero de equipo está
usando la computadora. Cuando sea su turno de usar la computadora, puede escribir el código lo
más rápido posible en lugar de perder tiempo pensando frente a la computadora).
• Si su compañero de equipo actualmente está codificando su algoritmo, prepare desafíos para su código
preparando datos de prueba de caso duro (esperemos que su código pase todos esos).
• Historia de fondo/descripción del problema. Por lo general, los problemas más fáciles se escriben
paraengañarconcursantes y hacer que parezca difícil, por ejemplo, agregando 'información adicional'
para crear una distracción. Los concursantes deben ser capaces defifiltrarestas
dieciséis
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
detalles sin importancia y centrarse en los esenciales. Por ejemplo, todos los párrafos iniciales
excepto la última oración en UVa 579 - ClockHands tratan sobre la historia del reloj y no tienen
ninguna relación con el problema real. Sin embargo, los problemas más difíciles generalmente
se escriben de la manera más sucinta posible: ya son lo suficientemente difíciles sin adornos
adicionales.
• Descripción de entrada y salida. En esta sección, se le darán detalles sobre cómo se formatea la
entrada y cómo debe formatear su salida. Esta parte generalmente se escribe de manera formal. Un
buen problema debe tener restricciones de entrada claras, ya que el mismo problema puede
resolverse con diferentes algoritmos para diferentes restricciones de entrada (consulte la Tabla 1.4).
• Muestra de entrada y muestra de salida. Los autores de problemas generalmente solo brindan
casos de prueba triviales a los concursantes. La entrada/salida de muestra está destinada a que los
concursantes verifiquen su comprensión básica del problema y verifiquen si su código puede analizar
la entrada dada usando el formato de entrada dado y producir la salida correcta usando el formato
de salida dado. No envíe su código al juez si ni siquiera pasa la entrada/salida de muestra dada.
Consulte la Sección 1.2.5 sobre cómo probar su código antes de enviarlo.
• Sugerencias o notas al pie. En algunos casos, los autores del problema pueden dejar sugerencias o agregar notas
al pie para describir el problema con más detalle.
• Los casos de prueba múltiples terminan con valores especiales (generalmente ceros).
• Los casos de prueba múltiples terminan con la señal EOF (fin de archivo).
17
1.3. PRIMEROS PASOS: LOS PROBLEMAS FÁCILES ©
c Steven y Félix
---------------------------------------------------------------------------------
int a, b; |12 |3
// scanf devuelve el número de elementos |57 | 12
leídos while (scanf("%d %d", &a, &b) == 2) |63 |9
// o puede verificar EOF, es decir |--------------|-------------- |
// while (scanf("%d %d", &a, &b) != EOF) |
printf("%d\n", a + b); | |
Algunos problemas con múltiples casos de prueba requieren que la salida de cada caso de prueba se numere
secuencialmente. Algunos también requieren una línea en blancodespuéscada caso de prueba. Modifiquemos el
problema simple anterior para incluir el número de caso en la salida (a partir de uno) con este formato de salida: "
Caso [NÚMERO]: [RESPUESTA]”seguido de una línea en blanco para cada caso de prueba. Suponiendo que la
entrada termina con la señal EOF, podemos usar el siguiente código:
Algunos otros problemas requieren que generemos solo líneas en blancoEntreCasos de prueba. Si usamos el
enfoque anterior, terminaremos con una nueva línea adicional al final de nuestra salida, produciendo un veredicto
de 'Error de presentación' (PE) innecesario. Deberíamos usar el siguiente código en su lugar:
Cambiemos un poco el problema simple de arriba. Para cada caso de prueba (cada línea de entrada), ahora
se nos da un número enterok(k≥1), seguido deknúmeros enteros Nuestra tarea ahora es generar la suma
de estosknúmeros enteros Suponiendo que la entrada termina con la señal EOF y no requerimos la
numeración de casos, podemos usar el siguiente código:
18
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
Ejercicio 1.3.1*: ¿Qué pasa si el autor del problema decide hacer la entradaun poco más ¿problemático? En lugar
de un número enterokal comienzo de cada caso de prueba, ahora debe sumar todos los números enteros en cada
caso de prueba (cada línea). Sugerencia: Consulte la Sección 6.2.
Ejercicio 1.3.2*: ¡Reescriba todo el código fuente C/C++ en esta Sección 1.3.2 en Java!
• Muy facil
Deberías tener estos problemas AC9en menos de 7 minutos10¡cada! Si es nuevo en la
programación competitiva, le recomendamos que comience su viaje resolviendo algunos
problemas de esta categoría después de completar la Sección 1.3.2 anterior. Nota: dado que
cada categoría contiene numerosos problemas para que los pruebe, tenemosresaltadoun
máximo de tres (3)Deberías intentar *problemas de cada categoría. Estos son los problemas
que, a nuestro juicio, son más interesantes o de mayor calidad.
• Fácil
Hemos dividido la categoría 'Fácil' en dos más pequeñas. Los problemas en esta categoría
siguen siendo fáciles, pero solo 'un poco' más difíciles que los 'Súper fáciles'.
1. UVa 00272 - Comillas TEX (reemplace todas las comillas dobles por comillas de estilo TEX())
2.UVa 01124 - Peligro de celebridad(LA 2681, solo haga eco/reimprima la entrada nuevamente)
9No te sientas mal si no puedes hacerlo. Puede haber muchas razones por las que un código no puede recibir AC.
10Siete minutos es solo una estimación aproximada. Algunos de estos problemas se pueden resolver con frases ingeniosas.
19
1.3. PRIMEROS PASOS: LOS PROBLEMAS FÁCILES ©
c Steven y Félix
8. UVa 11764 - Jumping Mario (un escaneo lineal para contar saltos altos y bajos)
9.UVa 11799 - Carrera de terror *(un escaneo lineal para encontrar el valor máximo)
10. UVa 11942 - Secuenciación de leñador (compruebe si la entrada está ordenada asc/descendente)
11. UVa 12015 - Google se siente afortunado (recorra la lista dos veces)
12UVa 12157 - Plan Tarifario(LA 4405, KualaLumpur08, calcular y comparar)
13UVa 12468 - Zapping(fácil; solo hay 4 posibilidades)
14UVa 12503 - Instrucciones del robot(simulación fácil)
15.UVa 12554 - Una canción especial...(simulación)
16. IOI 2010 - Cluedo (use 3 punteros)
17. IOI 2010 - Memoria (use 2 pases lineales)
• Medio: una muesca por encima de fácil (puede tomar de 15 a 30 minutos, pero no demasiado)
7. UVa 10919 - ¿Requisitos previos? (procesar los requisitos a medida que se lee la entrada)
8.UVa 11507 - Bender B. Rodríguez...*(simulación, si no)
9. UVa 11586 - Vías del tren (TLE si es fuerza bruta, encuentre el patrón)
10. UVa 11661 - ¿Hora de la hamburguesa? (escaneo lineal)
20
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
Las categorías:
• Carta de juego)
Hay muchos problemas Ad Hoc relacionados con juegos populares. Muchos están
relacionados con los juegos de cartas. Por lo general, necesitará analizar las cadenas de
entrada (consulte la Sección 6.3) ya que las cartas tienen ambos palos (D/Diamond/♦, C/
Club/♣, H/Corazón/♥, y S/Picas/♠) y rangos (generalmente: 2<3<. . .<9<T/Diez<J/Jack<Q/
Reina<K/Rey<A/A12). Puede ser una buena idea asignar estas cadenas problemáticas a
índices enteros. Por ejemplo, un mapeo posible es mapear D2→0, D3→1, . . . , AD→12, C2→
13, C3→14, . . . , SA→51. Entonces, puedes trabajar con los índices enteros.
• Juego (ajedrez)
El ajedrez es otro juego popular que a veces aparece en problemas de concursos de programación.
Algunos de estos problemas son ad hoc y se enumeran en esta sección. Algunos de ellos son
combinatorios con tareas como contar cuántas formas hay de colocar 8 reinas en 8×8 tablero de
ajedrez. Estos se enumeran en el Capítulo 3.
11Esto ya no fue así en IOI 2011-2012, ya que las puntuaciones más fáciles están dentro de la subtarea 1 de cada tarea.
12En algunos otros arreglos, A/Ace<2.
21
1.4. LOS PROBLEMAS AD HOC ©
c Steven y Félix
Bolos, etc. Puede ser útil conocer los detalles de estos juegos, pero la mayoría de las reglas del
juego se dan en la descripción del problema para evitar perjudicar a los concursantes que no
están familiarizados con los juegos.
• 'Derrochador de tiempo'problemas
Estos son problemas Ad Hoc que están escritos específicamente para hacer que la solución requerida
sea larga y tediosa. Estos problemas, si aparecen en un concurso de programación, determinarían el
equipo con máseficientecodificador: alguien que puede implementar soluciones complicadas pero
aún precisas bajo limitaciones de tiempo. Los entrenadores deberían considerar agregar tales
problemas en sus programas de entrenamiento.
– Los problemas ad hoc que involucran el uso de estructuras de datos lineales básicas (especialmente
matrices) se enumeran en la Sección 2.2.
– Los problemas ad hoc que implican cálculos matemáticos se enumeran en la Sección 5.2.
– Los problemas ad hoc relacionados con el procesamiento de cadenas se enumeran en la Sección 6.3.
– Los problemas ad hoc que involucran geometría básica se enumeran en la Sección 7.2.
22
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
• Carta de juego)
• Juego (ajedrez)
1. UVa 00255 - Movimiento correcto (comprueba la validez de los movimientos de ajedrez)
2.UVa 00278 - Ajedrez *(ad hoc, ajedrez, fórmula de forma cerrada existe)
3.UVa 00696 - Cuantos Caballeros *(ad hoc, ajedrez)
4. UVa 10196 - Check The Check (juego de ajedrez ad hoc, tedioso)
5.UVa 10284 - Tablero de ajedrez en FEN *(FEN = La notación de Forsyth-Edwards es una
notación estándar para describir las posiciones del tablero en un juego de ajedrez)
6. UVa 10849 - Mover el alfil (ajedrez)
7. UVa 11494 - Reina (ad hoc, ajedrez)
• Juego (Otros), Más fácil
1. UVa 00340 - Sugerencias de Master-Mind (determinar coincidencias fuertes y débiles)
2.UVa 00489 - Juez del verdugo *(solo haz lo que se te pide)
3.UVa 00947 - Ayudante de mente maestra(similar a UVa 340)
4.UVa 10189 - Buscaminas *(simular Buscaminas, similar a UVa 10279)
5. UVa 10279 - Mine Sweeper (una matriz 2D ayuda, similar a UVa 10189)
6. UVa 10409 - Juego de dados (solo simula el movimiento del dado)
7. UVa 10530 - Juego de adivinanzas (use una matriz de banderas 1D)
23
1.4. LOS PROBLEMAS AD HOC ©
c Steven y Félix
• Palíndromo
1. UVa 00353 - Pesky Palindromes (fuerza bruta en todas las subcadenas)
2.UVa 00401 - Palíndromos *(control palíndromo simple)
3. UVa 10018 - Invertir y sumar (comprobación ad hoc, matemáticas, palíndromo)
4.UVa 10945 - Madre Osa *(palíndromo)
5.UVa 11221 - Palíndromo Cuadrado Mágico *(tratamos con una matriz)
6. UVa 11309 - Counting Chaos (verificación de palíndromo)
• Anagrama
1. UVa 00148 - Verificador de anagramas (usa retroceso)
2.UVa 00156 - Anagrama *(más fácil conalgoritmo::ordenar)
3.UVa 00195 - Anagrama *(más fácil conalgoritmo::siguiente permutación)
4.UVa 00454 - Anagramas *(anagrama)
5. UVa 00630 - Anagramas (II) (ad hoc, cadena)
6. UVa 00642 - Amalgama de palabras (revisar el pequeño diccionario dado para ver la
lista de posibles anagramas)
7. UVa 10098 - Generación rápida, clasificada... (muy similar a UVa 195)
• Interesantes problemas de la vida real, más fáciles
24
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
16. UVa 11223 - O: dah, dah, dah (tediosa conversión de código morse)
17. UVa 11743 - Verificación de crédito (algoritmo de Luhn para verificar números de tarjetas de crédito; busque en
Internet para obtener más información)
18UVa 12342 - Calculadora de impuestos(el cálculo de impuestos puede ser realmente complicado)
• Tiempo
16. UVa 12136 - Horario de un hombre casado (LA 4202, Dhaka08, verifique la hora)
17UVa 12148 - Electricidad(fácil con el calendario gregoriano; use el método 'agregar'
para agregar un día a la fecha anterior y ver si es la misma que la fecha actual)
18UVa 12439 - 29 de febrero(exclusión inclusión; muchos casos de esquina; ten cuidado)
19UVa 12531 - Horas y Minutos(ángulos entre dos manecillas del reloj)
25
1.4. LOS PROBLEMAS AD HOC ©
c Steven y Félix
26
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
Ejercicio 1.1.2: Para una búsqueda completa ingenua como la que se describe en el cuerpo del texto, se necesitan
hastadieciséisC2×14C2× . . . ×2C2para el caso de prueba más grande connorte=8: demasiado grande. Sin embargo, hay
formas de reducir el espacio de búsqueda para que la búsqueda completa pueda seguir funcionando. Para un
desafío adicional, intenteEjercicio 1.1.3*!
1. (b) Use una estructura de datos de cola de prioridad (montón) (Sección 2.3).
3. Si la lista L es estática, (b) matriz simple que se procesa previamente con programación dinámica
(sección 2.2 y 3.5). Si la lista L es dinámica, entonces (g) Fenwick Tree es una mejor respuesta (más
fácil de implementar que (f) Segment Tree).
7. (b) El enfoque ingenuo anterior no funcionará. Debemos (primar) factorizarnorte! ymetroy ver si
los factores (primos) demetrose encuentra en los factores denorte! (Sección 5.5.5).
8. (b) No, debemos encontrar otra manera. Primero, encuentre el casco convexo delnortepuntos en O(norte
Iniciar sesiónnorte) (Sección 7.3.7). Sea el número de puntos enCH(S) =k. Como los puntos están dispersos
al azar,kserá mucho más pequeño quenorte. Luego, encuentre los dos puntos más lejanos examinando
todos los pares de puntos en elCH(S) enO(k2).
9. (b) El enfoque ingenuo es demasiado lento. ¡Utilice KMP o matriz de sufijos (Sección 6.4 o 6.6)!
27
1.5. SOLUCIONES A EJERCICIOS SIN ESTRELLA ©
c Steven y Félix
// Código Java para la tarea 1, suponiendo que se hayan realizado todas las importaciones
necesarias class Main {
public static void main(String[] args) {
Escáner sc = nuevo Escáner (System.in);
doble d = sc.nextDouble();
Sistema.salida.printf("%7.3f\n", d); // ¡sí, Java también tiene printf!
}}
// Código C++ para la tarea 2, suponiendo que se hayan realizado todas las inclusiones necesarias
int main() {
doble pi = 2 * acos(0.0); int n; // esta es una forma más precisa de calcular pi
escaneo("%d", &n);
printf("%.*lf\n", n, pi); // esta es la forma de manipular el ancho del campo
}
// Código Java para la tarea 3, suponiendo que se hayan realizado todas las importaciones
necesarias class Main {
public static void main(String[] args) {
Cadena[] nombres = nueva Cadena[]
{ "", "Dom LUN Mar MIE JUE VIE SAB" };
Calendario calendar = new GregorianCalendar(2010, 7, 9); // 9 de agosto de 2010
// tenga en cuenta que el mes comienza desde 0, por lo que debemos poner 7 en lugar
de 8 System.out.println(names[calendar.get(Calendar.DAY_OF_WEEK)]); // "Casarse"
}}
// Código C++ para la tarea 4, suponiendo que se hayan realizado todas las inclusiones necesarias
# definir TODO(x) x.begin(), x.end()
# define ÚNICO(c) (c).cambiar tamaño(único(TODOS(c)) - (c).begin())
int principal() {
int a[] = {1, 2, 2, 2, 3, 3, 2, 2, 1}; vector<int>
v(a, a + 9);
ordenar (TODO (v)); ÚNICO(v);
for (int i = 0; i < (int)v.size(); i++) printf("%d\n", v[i]);
}
// Código C++ para la tarea 5, suponiendo que se hayan realizado todas las inclusiones
necesarias typedef pair<int, int> ii; // utilizaremos el orden de clasificación natural //
par typedef<int, ii> iii; de los tipos de datos primitivos que emparejamos
int principal() {
iii A = hacer_par(ii(5, 24), -1982); iii B = // reordenar DD/MM/AAAA
hacer_par(ii(5, 24), -1980); iii C = // a MM, DD,
hacer_par(ii(11, 13), -1983); vector<iii> // y luego usa YYYY NEGATIVO
cumpleaños;
cumpleaños.push_back(A); cumpleaños.push_back(B); cumpleaños.push_back(C);
sort(cumpleaños.begin(), cumpleaños.end()); // eso es todo :)
}
28
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
// Código C++ para la tarea 6, suponiendo que se hayan realizado todas las inclusiones necesarias
int main() {
int n = 5, L[] = {10, 7, 5, 20, 8}, v = 7; ordenar(L, L
+ n);
printf("%d\n", binary_search(L, L + n, v));
}
// Código C++ para la tarea 7, suponiendo que se hayan realizado todas las inclusiones necesarias
int main() {
intp[10], N = 10; para (int i = 0; i < N; i++) p[i] = i; hacer {
}
while (siguiente_permutación(p, p + N));
}
// Código C++ para la tarea 8, suponiendo que se hayan realizado todas las inclusiones necesarias
int main() {
intp[20], N = 20;
para (int i = 0; i < N; i++) p[i] = i; para (int i =
0; i < (1 << N); i++) {
para (int j = 0; j < N; j++)
si (i & (1 << j)) // si el bit j está activado //
printf("%d", p[j]); esto es parte del conjunto
imprimirf("\n");
}}
// Código Java para la tarea 9, suponiendo que se hayan realizado todas las importaciones
necesarias class Main {
public static void main(String[] args) {
Cadena cadena = "FF"; entero X = 16, Y = 10;
System.out.println(nuevo BigInteger(str, X).toString(Y));
}}
// Código Java para la tarea 10, suponiendo que se hayan realizado todas las importaciones
necesarias class Main {
public static void main(String[] args) {
String S = "línea: a70 y z72 serán reemplazadas, aa24 y a872 no";
System.out.println(S.replaceAll("(^| )+[az][0-9][0-9]( |$)+", " *** "));
}}
// Código Java para la tarea 11, suponiendo que se hayan realizado todas las importaciones
necesarias class Main {
public static void main(String[] args) lanza Exception {
ScriptEngineManager mgr = nuevo ScriptEngineManager();
Motor ScriptEngine = mgr.getEngineByName("JavaScript"); // "hacer trampa"
29
1.5. SOLUCIONES A EJERCICIOS SIN ESTRELLA ©
c Steven y Félix
(a) Abandone este problema por otro.(No está bien, tu equipo perderá).
(b) Mejore el rendimiento de su solución.(Inútil.)
(c) Cree casos de prueba complicados para encontrar el error.(La respuesta más lógica.)
(d) (En concursos por equipos): Pida a su compañero de equipo que rehaga el problema.(Esto
podría ser factible ya que es posible que haya tenido algunas suposiciones incorrectas
sobre el problema. Por lo tanto, debe abstenerse de contarle los detalles del problema a
su compañero de equipo, quien volverá a resolver el problema. Aún así, su equipo perderá
un tiempo precioso).
(a) Abandone este problema por otro.(No está bien, tu equipo perderá).
(b) Mejore el rendimiento de su solución.(No está bien, no deberíamos obtener TLE con
unO(norte3)algoritmo sinorte≤400.)
(c) Cree casos de prueba complicados para encontrar el error.(Esta es la respuesta: tal vez su programa
se encuentre con un bucle infinito accidental en algunos casos de prueba).
4. Otro seguimiento de la Pregunta 2: ¿Qué pasa si el máximonortees 1.000, la salida solo depende del
tamaño de la entradanorte, y todavía tienescuatro horasde tiempo de competencia restante? (Si la
salida solo depende denorte, túmayoser capaz de calcular previamente todas las soluciones
posibles ejecutando suO(norte3)algoritmo en segundo plano, permitiendo que su compañero de
equipo use la computadora primero. Una vez que suO(norte3)solución termina, usted tiene
todas las respuestas. Enviar elO(1)responda en su lugar si no excede el 'límite de tamaño del
código fuente' impuesto por el juez).
6. Treinta minutos después del concurso, echas un vistazo a la tabla de líderes. Existen
muchos otros equipos que han resuelto un problemaXque su equipo no ha intentado.
¿Qué debes hacer?
(Un miembro del equipo debe intentar resolver el problema de inmediato).Xya que puede ser
relativamente fácil. Tal situación es realmente una mala noticia para su equipo, ya que es un revés
importante para obtener una buena clasificación en el concurso).
7. A la mitad del concurso, echas un vistazo a la tabla de líderes. El equipo líder (suponga que
no es su equipo) acaba de resolver el problemaY.¿Qué debes hacer? (Si su equipo no es el
que 'marca el ritmo', entonces es una buena idea 'ignorar' lo que está haciendo el
equipo líder y concentrarse en resolver los problemas que su equipo ha identificado
como 'solubles'. concurso, su equipo debe haber leído todos los problemas en el
conjunto de problemas e identificado aproximadamente los problemas que se
pueden resolver con las habilidades actuales de su equipo).
30
CAPÍTULO 1 INTRODUCCIÓN ©
c Steven y Félix
8. Su equipo ha dedicado dos horas a un problema desagradable. Has enviado varias implementaciones
por parte de diferentes miembros del equipo. Todas las presentaciones han sido juzgadas
incorrectas. No tienes idea de lo que está mal. ¿Qué debes hacer?
(Es hora de dejar de resolver este problema. No acapare la computadora, deje que su
compañero de equipo resuelva otro problema. O su equipo realmente no entendió el
problema o, en un caso muy raro, la solución del juez es realmente incorrecta. En
cualquier caso , esta no es una buena situación para su equipo).
9. Queda una hora para el final del concurso. Tienes 1 código WA y 1 idea nueva paraotro
problema. ¿Qué debe hacer usted (o su equipo)? (En la terminología del ajedrez, esto se
llama la situación de 'final del juego').
(a) Abandone el problema con el código WA, cambie al otro problema en un intento de
resolver un problema más.(Está bien en concursos individuales como IOI).
(b) Insista en que tiene que depurar el código WA. No hay suficiente tiempo para empezar
a trabajar en un nuevo problema.(Si la idea de otro problema implica un código
complejo y tedioso, entonces decidir centrarse en el código WA puede ser una
buena idea en lugar de tener dos soluciones incompletas/'no AC').
(c) (En ICPC): Imprima el código WA. Pida a otros dos miembros del equipo que lo analicen
mientras usted cambia a ese otro problema en un intento de resolverlo.dosmas
problemas. (Si la solución para el otro problema se puede codificar en menos de 30
minutos, impleméntela mientras sus compañeros de equipo intentan encontrar el
error del código WA estudiando la copia impresa).
Figura 1.4: Algunas referencias que inspiraron a los autores a escribir este libro
31
1.6. NOTAS DEL CAPÍTULO ©
c Steven y Félix
• Para mejorar su habilidad de mecanografía como se menciona en el Consejo 1, es posible que desee jugar a los muchos juegos de
• El consejo 2 está adaptado del texto de introducción en el portal de capacitación de USACO [48].
• Se pueden encontrar más detalles sobre el Consejo 3 en muchos libros de CS, por ejemplo, Capítulo 1-5, 17 de [7].
• Para obtener más información sobre mejores pruebas (Consejo 5), puede valer la pena intentar un pequeño desvío a los
libros de ingeniería de software.
– fuerzas de código,http://codeforces.com/,
– Juez en línea de la Universidad de Pekín, (POJ)http://poj.org,
– Juez en línea de la Universidad de Zhejiang, (ZOJ)http://acm.zju.edu.cn,
– Juez en línea de la Universidad de Tianjin,http://acm.tju.edu.cn/toj,
– Juez en línea de la Universidad Estatal de los Urales (Timus),http://acm.timus.ru,
– Juez en línea URI,http://www.urionlinejudge.edu.br,etc.
• Para obtener una nota sobre la competencia por equipos (Consejo 7), lea [16].
32
Capitulo 2
1Incluso en esta tercera edición,todavíautiliza principalmente código C++ para ilustrar técnicas de implementación. Los
equivalentes de Java se pueden encontrar en el sitio web de apoyo de este libro.
2Los materiales en la Sección 2.2-2.3 generalmente se cubren en el primer añoEstructuras de datoscurrículos de CS. Se alienta a
los estudiantes de secundaria que aspiran a participar en el IOI a participar en el estudio independiente de dicho material.
33
2.1. VISIÓN GENERAL Y MOTIVACIÓN ©
c Steven y Félix
JavaÁrbolMapa)implementaciones para almacenar colecciones dinámicas de pares clave-datos sin comprender que
la estructura de datos subyacente es unÁrbol de búsqueda binario balanceado, o use el STL de C++cola de
prioridad (o Javacola de prioridad)ordenar una cola de elementos sin comprender que la estructura de datos
subyacente es un(generalmente Binario) Montón. Ambas estructuras de datos generalmente se enseñan en los
planes de estudio de Ciencias de la Computación del primer año.
Este capitulo esta dividido en tres partes. La sección 2.2 contiene información básicalinealestructuras de
datos y las operaciones básicas que soportan. Sección 2.3 cubre básicono linealestructuras de datos tales
como árboles de búsqueda binarios (BST) (equilibrados), montones (binarios) y tablas hash, así como sus
operaciones básicas. La discusión de cada estructura de datos en la Sección 2.2-2.3 es breve, con énfasis en
la importanterutinas de la bibliotecaque existen para manipular las estructuras de datos. Sin embargo, las
estructuras de datos especiales que son comunes en los concursos de programación, como la máscara de
bits y varias técnicas de manipulación de bits (vea la figura 2.1), se analizan con más detalle. La Sección 2.4
contienemásestructuras de datos para las que no existe una implementación incorporada y, por lo tanto,
requieren que construyamosnuestra propiabibliotecas La Sección 2.4 tiene una discusión más profunda que
la Sección 2.2-2.3.
Como este capítulo es el primero que se sumerge en el corazón de la programación competitiva, ahora
aprovecharemos la oportunidad para resaltar varias características de valor agregado de este libro que verá
en este capítulo y en los siguientes.
Una característica clave de este libro es la colección que lo acompaña deejemplos eficientes y
completamente implementadostanto en C/C++ como en Java del que carecen muchos otros libros de
Ciencias de la Computación, deteniéndose en el 'nivel de pseudocódigo' en su demostración de estructuras
de datos y algoritmos. Esta característica ha estado en el libro desde la primera edición. Las partes
importantes del código fuente se han incluido en el libro.3y el código fuente completo está alojado en
sites.google.com/site/stevenhalim/home/material.La referencia a cada archivo de origen se indica en el
cuerpo del texto como un cuadro como el que se muestra a continuación.
Otro punto fuerte de este libro es la colección de ejercicios escritos y de programación (en su mayoría
respaldados por UVa Online Judge [47] e integrados con uHunt; consulte el Apéndice A). En elterceraedición,
hemos añadidomucho masejercicios escritos. Hemos clasificado los ejercicios escritos ensin estrellay
sembrado de estrellasunos. Los ejercicios escritos sin asterisco están destinados a ser utilizados
principalmente con fines de autoevaluación; Las soluciones se dan al final de cada capítulo. Los ejercicios
escritos con estrellas se pueden usar para desafíos adicionales; no proporcionamos soluciones para estos,
pero en su lugar podemos proporcionar algunos consejos útiles.
En elterceraedición, hemos agregado visualizaciones4para muchas estructuras de datos y algoritmos
cubiertos en este libro [27]. Creemos que estas visualizaciones serán de gran beneficio para los estudiantes
visuales en nuestra base de lectores. En este momento (24 de mayo de 2013), las visualizaciones están
alojadas en:www.comp.nus.edu.sg/∼stevenha/visualización.La referencia a cada visualización se incluye en
el cuerpo del texto como un cuadro como el que se muestra a continuación.
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización
3Sin embargo, hemos optado por no incluir el código de la Sección 2.2-2.3 en el cuerpo del texto porque en su mayoría
son 'triviales' para muchos lectores, excepto quizás por algunos trucos útiles.
4Están construidos con lienzo HTML5 y tecnología JavaScript.
34
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
• Matriz redimensionable dinámicamente: C++ STLvectores (JavaLista de arreglo (más rápido) oVector)
Esta estructura de datos es similar a la matriz estática, excepto que está diseñada para manejar el
cambio de tamaño en tiempo de ejecución de forma nativa. Es mejor usar unvectoren lugar de una
matriz si se desconoce el tamaño de la secuencia de elementos en tiempo de compilación. Por lo
general, inicializamos el tamaño (reserva()oredimensionar())con el tamaño estimado de la colección
para un mejor rendimiento. STL típico de C++vectorLas operaciones utilizadas en la programación
competitiva incluyen retroceder(), en(),el operador,asignar(), borrar(), borrar(),yiteradors para
atravesar el contenido devectors.
5Sin embargo, a veces necesitamos 'reinventar la rueda' para ciertos problemas relacionados con la clasificación, por ejemplo, el
problema del Índice de Inversión en la Sección 9.14.
35
2.2. DS LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
En general, existen tres métodos comunes para buscar un elemento en una matriz:
1.O(norte) Búsqueda lineal: considere todos los elementos desde el índice 0 hasta el índicenorte -1 (evítelo
siempre que sea posible).
2.O(Iniciar sesiónnorte) Búsqueda binaria: Usolímite inferior, límite superior,obúsqueda binariaen C++
STLalgoritmo (o JavaColecciones.binarySearch).Si la matriz de entrada no está ordenada, es necesario
ordenar la matriz al menos una vez (utilizando uno de losO(norteIniciar sesiónnorte) algoritmo de
clasificación anterior) antes de ejecutar uno (omuchos) Búsqueda(s) binaria(s).
3.O(1) con Hashing: esta es una técnica útil para usar cuando se requiere un acceso rápido a valores
conocidos. Si se selecciona una función hash adecuada, la probabilidad de que se produzca una
colisión es insignificantemente pequeña. Aún así, esta técnica rara vez se usa y podemos vivir sin ella.
6para la mayoría de los problemas (de concurso).
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización/clasificación.html
Código fuente:ch2 02 algoritmo colecciones.cpp/java
Si nuestra matriz solo necesita contener valores booleanos (1/verdadero y 0/falso), podemos usar una
estructura de datos alternativa que no sea una matriz: un C++ STLconjunto de bitslosconjunto de bits
soporta operaciones útiles comorestablecer(), establecer(),el operador [] yprueba().
• Máscaras de bits, también conocidas como conjuntos ligeros y pequeños de booleanos (soporte nativo en C/C++/
Java) Un número entero se almacena en la memoria de una computadora como una secuencia/cadena de bits. Por
lo tanto, podemos usar números enteros para representar unligeropequeño conjunto de valores booleanos. Todas
las operaciones de conjunto involucran solo la manipulación bit a bit del entero correspondiente, lo que lo
convierte en unmucho más eficienteelección en comparación con C++ STLvector<bool>, conjunto de bits,o
establecer<En t>opciones Tal velocidad es importante en la programación competitiva. AlgunoLas operaciones
importantes que se utilizan en este libro se muestran a continuación.
6Sin embargo, las preguntas sobre hash aparecen con frecuencia en las entrevistas para trabajos de TI.
7Para evitar problemas con la representación del complemento a dos, utilice un 32-bit/64-bitfirmadoentero para representar máscaras
de bits de hasta 30/62 elementos solamente, respectivamente.
36
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
37
2.2. DS LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
7. Para obtener el valor del bit menos significativo que está activado (primero desde la
derecha), useT = (S & (-S)).
Ejemplo para n = 3
S + 1 = 8 (base 10) = 1000 <- el bit '1' se desplaza a la izquierda 3 veces
1
-------
S = 7 (base 10) = 111 (base 2)
Ejemplo para n = 5
S + 1 = 32 (base 10) = 100000 <- el bit '1' se desplaza a la izquierda 5 veces
1
---------
S = 31 (base 10) = 11111 (base 2)
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización/máscara de bits.html
Código fuente:ch2 manipulación de 03 bits.cpp/java
Muchas operaciones de manipulación de bits se escriben como macros de preprocesador en nuestro código
fuente de ejemplo de C/C++ (pero se escriben claramente en nuestro código de ejemplo de Java, ya que Java no
admite macros).
38
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
La única excepción es probablemente UVa 11988 - Teclado roto (también conocido como Beiju Text),
donde debe mantener dinámicamente una lista (vinculada) de caracteres e insertar de manera
eficiente un nuevo carácteren cualquier sitioen la lista, es decir, al frente (cabeza), actual o atrás (cola)
de la lista (enlazada). De los problemas UVa de 1903 que los autores han resuelto, es probable que
este sea el único problema de lista enlazada pura que hemos encontrado hasta ahora.
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización/lista.html
Código fuente:ch2 04 cola de pila.cpp/java
4*. Imprime los enteros enSque caen entre un rango [una . . . b] (inclusive) en orden
39
2.2. DS LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
Ejercicio 2.2.2: Hay varios otros trucos 'geniales' posibles con técnicas de manipulación de
bits, pero rara vez se usan. Implemente estas tareas con manipulación de bits:
8*. Invirtamos el problema UVa 11173 anterior. Dado un código gris, encuentre su posiciónk
utilizando la manipulación de bits.
Ejercicio 2.2.4*: Podemos usar una lista enlazada (C++ STLlistao JavaLista enlazada)para implementar una
cola eficiente (o deque). Averigua cómo lograr esto. Pregunta de seguimiento: ¿Podemos usar una matriz
redimensionable en su lugar? ¿Por qué o por qué no?
Ejercicios de programación que involucran estructuras de datos lineales (y algoritmos) con bibliotecas:
1.UVa 00230 - Prestatarios(un poco de análisis de cadenas, vea la Sección 6.2; mantener una lista de libros
ordenados; clave de clasificación: los nombres de los autores primero y, si hay empates, por título; el tamaño
de entrada es pequeño aunque no se indica; no necesitamos usar BST balanceado)
5. UVa 00482 - Matrices de permutación (es posible que deba usar un tokenizador de cadena,
consulte la Sección 6.2, ya que no se especifica el tamaño de la matriz)
6. UVa 00591 - Caja de Ladrillos (suma todos los elementos; obtenga el promedio; sume las
diferencias absolutas totales de cada elemento del promedio dividido por dos)
7.UVa 00665 - Moneda Falsa(use banderas booleanas 1D; todas las monedas son inicialmente monedas
falsas potenciales; si '=', todas las monedas a la izquierda y derecha no son monedas falsas; si '<' o '>',
todas las monedas que no están a la izquierda y a la derecha no son monedas falsas; verifique si solo
queda una moneda falsa candidata al final)
8. UVa 00755 - 487-3279 (Tabla de direccionamiento directo; convierta las letras excepto Q y Z a 2-9;
mantenga '0'-'9' como 0-9; ordene los números enteros; encuentre duplicados si los hay)
40
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
9.UVa 10038 - Jolly Jumpers *(use banderas booleanas 1D para verificar [1..norte -1])
10. UVa 10050 - Hartales (bandera booleana 1D)
11. UVa 10260 - Soundex (Tabla de direccionamiento directo para mapeo de código soundex)
12. UVa 10978 - Let's Play Magic (manipulación de matriz de cadenas 1D)
13UVa 11093 - Solo termínalo(escaneo lineal, matriz circular, un poco desafiante)
14. UVa 11192 - Grupo inverso (matriz de caracteres)
15. UVa 11222: solo lo hice yo (use varias matrices 1D para simplificar este problema)
dieciséis.UVa 11340 - Periódico *(DAT; ver Hashing en la Sección 2.3)
17. UVa 11496 - Musical Loop (almacenar datos en matriz 1D, contar los picos)
18. UVa 11608: sin problemas (use tres matrices: creada; requerida; disponible)
19. UVa 11850 - Alaska (para cada ubicación de número entero de 0 a 1322; ¿puede Brenda llegar a
(cualquier lugar dentro de las 200 millas) de cualquier estación de carga?)
• Manipulación de matriz 2D
1. UVa 00101 - El problema de los bloques (simulación similar a la 'pila'; pero también necesitamos
acceder al contenido de cada pila, por lo que es mejor usar una matriz 2D)
2. UVa 00434 - Matty's Blocks (un tipo de problema de visibilidad en geometría, solucionable con el
uso de manipulación de matriz 2D)
7.UVa 10855 - Cuadrados rotados *(matriz de cadenas, 90orotación en el sentido de las agujas del reloj)
3. UVa 00400 - Unix ls (este comando se usa con mucha frecuencia en UNIX)
4. UVa 00450 - Little Black Book (tedioso problema de clasificación)
5.UVa 00790 - Cefalea del juez principal(similar a UVa 10258)
6. UVa 00855 - Almuerzo en Grid City (clase, mediana)
7. UVa 01209 - Wordfish (LA 3173, Manila06) (STLSiguienteypermutación anterior)
8.UVa 10057 - Una noche de verano...(involucra la mediana, use STLordenar, límite
superior, límite inferiory algunos cheques)
41
2.2. DS LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
• Manipulación de bits (ambos C++ STLconjunto de bits (Javaconjunto de bits)y máscara de bits)
1. UVa 00594 - One Little, Two Little... (manipular cadena de bits conconjunto de bits)
2. UVa 00700 - Errores de fecha (se puede resolver conconjunto de bits)
7.UVa 11926 - Multitarea *(usar 1Mconjunto de bitspara verificar si una ranura está libre)
8.UVa 11933 - Partición de números *(un ejercicio de manipulación de bits)
9. IOI 2011 - Palomas (este problema se simplifica con la manipulación de bits, pero la
solución final requiere mucho más que eso).
42
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
• Árbol de búsqueda binaria equilibrada (BST): C++ STLmapa/conjunto (JavaMapa de árbol/Conjunto de árbol)
El BST es una forma de organizar los datos en una estructura de árbol. En cada subárbol con raíz enX, se
cumple la siguiente propiedad BST: Elementos en el subárbol izquierdo deXson más pequeños queX y
elementos en el subárbol derecho deXson mayores que (o iguales a)X. Esta es esencialmente una aplicación
de la estrategia Divide y vencerás (ver también la Sección 3.3). Organizar los datos de esta manera (ver
Figura 2.2) permiteO(Iniciar sesiónnorte)buscar(clave), insertar(clave), findMin()/findMax(), sucesor(clave)/
predecesor(clave),yeliminar (clave)ya que en el peor de los casos, sóloO(Iniciar sesiónnorte) se requieren
operaciones en un escaneo de raíz a hoja (ver [7, 5, 54, 12] para más detalles). Sin embargo, esto solo es
válido si el BST está equilibrado.
11El contenido de una estructura de datos dinámica se modifica con frecuencia mediante operaciones de inserción/eliminación/actualización.
12El árbol AVL fue el primer BST autoequilibrado que se inventó. Los árboles AVL son esencialmente BST tradicionales con una
propiedad adicional: las alturas de los dos subárboles de cualquier vértice en un árbol AVL pueden diferir por como mucho uno.
Las operaciones de reequilibrio (rotaciones) se realizan (cuando es necesario) durante las inserciones y eliminaciones para
mantener esta propiedad invariable y, por lo tanto, mantener el árbol más o menos equilibrado.
13El árbol rojo-negro es otro BST autoequilibrado, en el que cada vértice tiene un color: rojo o negro. En los árboles RB, el vértice
de la raíz, todos los vértices de las hojas y los dos hijos de cada vértice rojo son negros. Todo camino simple desde un vértice a
cualquiera de sus hojas descendientes contieneel mismo número de vértices negros. A lo largo de las inserciones y eliminaciones,
un árbol RB mantendrá todas estas invariantes para mantener el árbol equilibrado.
43
2.3. DS NO LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
STLestablecer (y Javaconjunto de árboles)solo almacena la clave. Para la mayoría de los problemas (de
concurso), usamos unmapa (mapear realmente la información) en lugar de unestablecer (aestablecersolo
es útil para determinar de manera eficiente la existencia de una determinada clave). Sin embargo, hay un
pequeño inconveniente. Si usamos las implementaciones de la biblioteca, se vuelve difícil o imposible
aumentar (agregar información adicional) al BST. Por favor intenteEjercicio 2.3.5*y lea la Sección 9.29 para
obtener más detalles.
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización/bst.html
Código fuente:ch2 05 conjunto de mapas.cpp/java
En lugar de aplicar la propiedad BST, el Montón (Max) aplica la propiedad Montón: en cada subárbol
enraizado enX, artículos a la izquierdaysubárboles derechos deXson menores que (o iguales a)X(ver
Figura 2.3). Esta es también una aplicación del concepto Divide y vencerás (ver Sección 3.3). La
propiedad garantiza que la parte superior (o raíz) del montón sea siempre el elemento máximo. No
existe la noción de una 'búsqueda' en el Heap (a diferencia de los BST). En cambio, Heap permite la
extracción rápida (eliminación) del elemento máximo:ExtraerMax()e inserción de nuevos elementos:
Insertar (v)—ambos de los cuales se pueden lograr fácilmente en unO(Iniciar sesiónnorte) transversal
de raíz a hoja o de hoja a raíz, realizando operaciones de intercambio para mantener la propiedad
(Max) Heap siempre que sea necesario (ver [7, 5, 54, 12] para más detalles).
El montón (máximo) es una estructura de datos útil para modelar una cola de prioridad, donde el
elemento con la prioridad más alta (el elemento máximo) se puede quitar de la cola (Extraer Max())
14Un árbol binario completo es un árbol binario en el que todos los niveles, excepto posiblemente el último, están completamente llenos. Todos los
vértices del último nivel también deben rellenarse de izquierda a derecha.
44
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
y un nuevo artículovse puede poner en cola (Insertar (v)),ambos enO(Iniciar sesiónnorte) tiempo. La
implementación15decola de prioridadestá disponible en C++ STLcolabiblioteca (o Java cola de
prioridad).Las colas de prioridad son un componente importante en algoritmos como los algoritmos
de Prim (y Kruskal) para el problema del árbol de expansión mínimo (MST) (consulte la Sección 4.3), el
algoritmo de Dijkstra para el problema de las rutas más cortas de fuente única (SSSP) (consulte la
Sección 4.4.3) , y el algoritmo de búsqueda A* (consulte la Sección 8.2.5).
Esta estructura de datos también se utiliza para realizarclasificación parcialen C++ STLalgoritmo biblioteca. Una posible
implementación es procesar los elementos uno por uno y crear un Maxdieciséismontón dekelementos, eliminando el
elemento más grande siempre que su tamaño excedak(kes el número de elementos solicitados por el usuario). El mas
pequeñokLuego, los elementos se pueden obtener en orden descendente quitando la cola de los elementos restantes en
Max Heap. Como cada operación de dequeue esO(Iniciar sesiónk),clasificación parcialposeeO(norteIniciar sesiónk)
complejidad del tiempo17. Cuandok=norte, este algoritmo es equivalente a una clasificación de montón. Tenga en cuenta
que aunque la complejidad temporal de una clasificación de montón también esO(norteIniciar sesiónnorte), las
clasificaciones de almacenamiento dinámico suelen ser más lentas que las clasificaciones rápidas porque las operaciones
de almacenamiento dinámico acceden a los datos almacenados en índices distantes y, por lo tanto, no son compatibles
con la memoria caché.
Visualización:www.comp.nus.edu.sg/∼stevenha/visualización/montón.html
Código fuente:ch2 06 cola de prioridad.cpp/java
Sin embargo, se puede usar una forma simple de tablas hash en concursos de programación. Las
'tablas de direccionamiento directo' (DAT) pueden considerarse tablas hash en las que las propias
claves son los índices, o en las que la 'función hash' es la función de identidad. Por ejemplo, es
posible que necesitemos asignar todos los caracteres ASCII posibles [0-255] a valores enteros, por
ejemplo, 'a'→'3', 'W'→'10', . . . , 'YO'→'13'. Para este propósito, no necesitamos el C++ STLmapao
cualquier forma de hashing como la clave en sí (el valor del carácter ASCII) es único y suficiente para
determinar el índice apropiado en una matriz de tamaño 256. Algunos ejercicios de programación
que involucran DAT se enumeran en la Sección 2.2 anterior.
15El STL predeterminado de C++cola de prioridades un Max Heap (la eliminación de la cola produce elementos en orden de clave
descendente) mientras que el Java predeterminadoPriorityQueuees un Min Heap (produce elementos en orden de clave
ascendente). Sugerencias: un Max Heap que contiene números se puede convertir fácilmente en un Min Heap (y viceversa)
insertando las claves negadas. Esto se debe a que negar un conjunto de números invertirá su orden de aparición cuando se
clasifiquen. Este truco se usa varias veces en este libro. Sin embargo, si la cola de prioridad se utiliza para almacenarenteros con
signo de 32 bits, se producirá un desbordamiento si−231se niega como 231−1 es el valor máximo de un entero de 32 bits con signo.
dieciséisEl valor por defectoclasificación parcialproduce el más pequeñokelementos en orden ascendente.
17Es posible que haya notado que la complejidad del tiempoO(norteIniciar sesiónk) dóndekes el tamaño de salida ynortees el tamaño de
entrada. Esto significa que el algoritmo es 'sensible a la salida' ya que su tiempo de ejecución depende no solo del tamaño de entrada sino
también de la cantidad de elementos que tiene que generar.
18Tenga en cuenta que C ++ 11 es un nuevo estándar de C ++, es posible que los compiladores más antiguos aún no lo admitan.
45
2.3. DS NO LINEAL CON BIBLIOTECAS INTEGRADAS ©
c Steven y Félix
Ejercicio 2.3.1: Alguien sugirió que es posible almacenar la clave→pares de valores en unmatriz
ordenadadeestructuras para que podamos usar elO(Iniciar sesiónnorte) búsqueda binaria del
problema de ejemplo anterior. ¿Es factible este enfoque? Si no, ¿cuál es el problema?
Ejercicio 2.3.2: No discutiremos los conceptos básicos de las operaciones BST en este libro. En su lugar,
utilizaremos una serie de subtareas para verificar su comprensión de los conceptos relacionados con BST.
Usaremos la figura 2.2 comoreferencia inicialen todas las subtareas excepto la subtarea 2.
Ejercicio 2.3.3*: Supongamos que se le da una referencia a la raízRde un árbol binario Tque
contienenortevértices. Puede acceder a los vértices primario, izquierdo y derecho de un nodo,
así como a su clave a través de su referencia. Resuelva cada una de las siguientes tareas con los
mejores algoritmos posibles que se le ocurran y analice sus complejidades de tiempo.
Supongamos las siguientes restricciones: 1≤norte≤100kde modo queO(norte2) las soluciones
son teóricamente inviables en un entorno de concurso.
2*. Salida de los elementos enTque están dentro de un rango dado [a..b] en orden ascendente.
Ejercicio 2.3.4*: Se sabe que el recorrido en orden (consulte también la Sección 4.7.2) de un BST estándar
(no necesariamente balanceado) produce el elemento del BST en orden ordenado y se ejecuta enO(norte).
¿El siguiente código también produce los elementos BST en orden? ¿Se puede hacer funcionar en un
tiempo total deO(norte) en vez deO(Iniciar sesiónnorte+ (norte -1)×Iniciar sesiónnorte) = O(norteIniciar
sesiónnorte)? Si es posible, ¿cómo?
x = encontrarMin(); salida
x para (i = 1; i < n; i++) // ¿Este bucle es O(n log n)?
x = sucesor(x); salida x
Ejercicio 2.3.6: No discutiremos los conceptos básicos de las operaciones Heap en este libro. En su lugar,
utilizaremos una serie de preguntas para verificar su comprensión de los conceptos de Heap.
46
CAPÍTULO 2. ESTRUCTURAS DE DATOS Y BIBLIOTECAS ©
c Steven y Félix
1. Con la Figura 2.3 como montón inicial, muestre los pasos tomados porInsertar (26).
Ejercicio 2.3.7: ¿La estructura está representada por una matriz compacta basada en 1 (ignorando el índice
0) ordenada en orden descendente como Max Heap?
Ejercicio 2.3.8*: Demuestre o refute esta afirmación: “El segundo elemento más grande en un Max
Heap connorte≥3 elementos distintos es siempre uno de los hijos directos de la raíz”. Pregunta de
seguimiento: ¿Qué pasa con el tercer elemento más grande? ¿Dónde está(n) la(s) ubicación(es)
potencial(es) del tercer elemento más grande en un Max Heap?
Ejercicio 2.3.9*: Dada una matriz compacta basada en 1Aque contienenorteenteros (1≤norte≤
100k) que están garantizados para satisfacer la propiedad Max Heap, generan los elementos en
Aque son mayores que un enterov. ¿Cuál es el mejor algoritmo?
Ejercicio 2.3.11*: una operación de montónnosoportado directamente por C++ STL cola de
prioridad (y Javacola de prioridad)es elActualizar clave (índice, nueva clave)operación, que
permite actualizar (aumentar o disminuir) el elemento Heap (Max) en un cierto índice.
Escribetu propioImplementación binaria (Max) Heap con esta operación.
Ejercicio 2.3.12*: Otra operación de montón que puede ser útil es laDeleteKey(índice) operación para
eliminar (Max) elementos Heap en un índice determinado. ¡Implemente esto!
Ejercicio 2.3.13*: Supongamos que solo necesitamos elDecreaseKey (índice, nueva clave) operación,
es decir, unaActualizar claveoperación donde la actualizaciónsiemprehacenueva llavemenor que su
valor anterior. ¿Podemos usar un enfoque más simple que enEjercicio 2.3.11? Sugerencia: Use la
eliminación diferida, usaremos esta técnica en nuestro código Dijkstra en la Sección 4.4.3.
Ejercicio 2.3.14*: ¿Es posible utilizar un BST equilibrado (por ejemplo, C++ STLestablecero Java conjunto de
árboles)para implementar una cola de prioridad con el mismoO(Iniciar sesiónnorte) enqueue y dequeue el
rendimiento? Si es así, ¿cómo? ¿Existen posibles inconvenientes? Si no, ¿por qué?
Ejercicio 2.3.15*: ¿Hay una mejor manera de implementar una cola de prioridad si las claves son todos
números enteros dentro de un rango pequeño, por ejemplo, [0. . .100]? estamos esperando unO(1)
rendimiento en cola y de cola. Si es así, ¿cómo? Si no, ¿por qué?
Ejercicio 2.3.16: ¿Qué estructura de datos no lineal debe usar si tiene que admitir las
siguientes tres operaciones dinámicas: 1) muchas inserciones, 2) muchas
eliminaciones y 3) muchas solicitudes de datos en orden?
47
2.3. DS NO LINEAL CON BIBLIOTECAS INTEGRADAS ©