Академический Документы
Профессиональный Документы
Культура Документы
Prólogo 15
1. Lenguajes 17
1.1. C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.2. Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.3. Tipo de dato . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.3.1. C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.3.2. Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.4. Formato de entrada y salida . . . . . . . . . . . . . . . . . . . 35
1.4.1. Entrada . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.4.2. Salida . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.5. Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
1.5.1. Operadores Aritméticos . . . . . . . . . . . . . . . . . 59
1.5.2. Operadores de Asignación . . . . . . . . . . . . . . . . 60
1.5.3. Operadores de Relación . . . . . . . . . . . . . . . . . 60
1.5.4. Operadores Lógicos . . . . . . . . . . . . . . . . . . . . 60
1.5.5. Operadores Relacionados con punteros (C++) . . . . . 61
1.5.6. Operadores de Estructuras y Uniones (C++) . . . . . . 61
1.5.7. Operadores Lógicos y de Desplazamiento de Bits . . . . 61
1.5.8. Misceláneas (C++) . . . . . . . . . . . . . . . . . . . . 61
1.6. Estructuras de control condicional e iterativa . . . . . . . . . . 61
1.6.1. Estructura de control condicional (Bifurcaciones) . . . 61
1.6.2. Estructura de control iterativa (Bucles) . . . . . . . . . 66
1.6.3. Sentencias break, continue, goto . . . . . . . . . . . . . 68
1.7. Variantes de entradas de datos . . . . . . . . . . . . . . . . . . 69
1.7.1. Entrada simple . . . . . . . . . . . . . . . . . . . . . . 69
1.7.2. Entrada por casos de pruebas . . . . . . . . . . . . . . 72
1.7.3. Entrada por condición . . . . . . . . . . . . . . . . . . 72
1.7.4. Entrada hasta fin de ficheros . . . . . . . . . . . . . . . 72
1.8. Variantes de salidas de datos . . . . . . . . . . . . . . . . . . . 72
3
4 ÍNDICE GENERAL
2. Add Hoc 73
2.1. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 74
3. Fuerza Bruta 87
3.1. Complejidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.1.1. Eficiencia temporal . . . . . . . . . . . . . . . . . . . . 88
3.2. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 91
4. Aritmética y Álgebra 95
4.1. Multplicación de matrices . . . . . . . . . . . . . . . . . . . . 95
4.2. Exponenciación binaria . . . . . . . . . . . . . . . . . . . . . . 98
4.3. Métodos numéricos . . . . . . . . . . . . . . . . . . . . . . . . 99
4.3.1. Búsqueda ternaria . . . . . . . . . . . . . . . . . . . . 100
4.3.2. Newton-Raphson . . . . . . . . . . . . . . . . . . . . . 102
4.4. Problema de Flavio Josefo . . . . . . . . . . . . . . . . . . . . 104
4.5. Máximo común divisor . . . . . . . . . . . . . . . . . . . . . . 106
4.6. Algoritmo Euclidiano Extendido . . . . . . . . . . . . . . . . . 108
4.6.1. Implementación . . . . . . . . . . . . . . . . . . . . . . 109
4.7. Ecuación lineal de diofantina . . . . . . . . . . . . . . . . . . . 110
4.7.1. Encontrar una solución . . . . . . . . . . . . . . . . . . 110
4.7.2. Encontrar el número de soluciones y las soluciones en
un intervalo dado . . . . . . . . . . . . . . . . . . . . . 112
4.7.3. Encuentre la solución con un valor mı́nimo de x + y . . 114
4.8. Inverso Multiplicativo Modular . . . . . . . . . . . . . . . . . 114
4.8.1. Encontrar el inverso modular usando el algoritmo eu-
clidiano extendido . . . . . . . . . . . . . . . . . . . . . 115
4.8.2. Encontrar el inverso modular usando exponenciación
binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
4.8.3. Encontrar el inverso modular para cada número módu-
lo m . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
4.9. Mı́nimo común múltiplo . . . . . . . . . . . . . . . . . . . . . 119
4.10. Sucesiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.10.1. Sucesiones aritméticas . . . . . . . . . . . . . . . . . . 119
4.10.2. Sucesiones geométricas . . . . . . . . . . . . . . . . . . 119
4.10.3. Sucesiones telescópicas . . . . . . . . . . . . . . . . . . 119
4.10.4. Técnica de interpolación . . . . . . . . . . . . . . . . . 119
4.10.5. Método de Langrange . . . . . . . . . . . . . . . . . . 119
4.10.6. Método de Newton . . . . . . . . . . . . . . . . . . . . 119
4.10.7. Tabla de diferencias dividida . . . . . . . . . . . . . . . 119
4.10.8. Sucesiones de recurrencias . . . . . . . . . . . . . . . . 119
4.11. Matrix Exponentiation . . . . . . . . . . . . . . . . . . . . . . 119
ÍNDICE GENERAL 5
5. Ordenamiento 127
5.1. Merge Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.2. Heap Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
5.3. Quick Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
5.4. Radix Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
5.5. Burble Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.6. CountingSort . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.7. Insertion Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.8. Selection Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.9. Bibliotecas con funciones de ordenamiento . . . . . . . . . . . 140
5.9.1. Bibliotecas de C++ . . . . . . . . . . . . . . . . . . . . 140
5.9.2. Bibliotecas de Java . . . . . . . . . . . . . . . . . . . . 143
5.10. Ordenamiento de datos personalizados . . . . . . . . . . . . . 145
5.10.1. C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
5.10.2. Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
5.11. Change frequency of bubble sort . . . . . . . . . . . . . . . . . 151
5.12. Select k th element . . . . . . . . . . . . . . . . . . . . . . . . 151
5.13. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 151
6 ÍNDICE GENERAL
6. Búsqueda 157
6.1. Búsqueda binaria . . . . . . . . . . . . . . . . . . . . . . . . . 157
6.2. Bibliotecas con funciones de búsqueda . . . . . . . . . . . . . 159
6.2.1. Bibliotecas de C++ . . . . . . . . . . . . . . . . . . . . 159
6.2.2. Bibliotecas de Java . . . . . . . . . . . . . . . . . . . . 161
6.3. Selección rápida . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.4. Búsqueda Exhaustiva . . . . . . . . . . . . . . . . . . . . . . . 163
6.5. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 163
7. Cadena 169
7.1. Operaciones básicas . . . . . . . . . . . . . . . . . . . . . . . . 169
7.2. Algoritmos de búsqueda de cadena . . . . . . . . . . . . . . . 170
7.2.1. Algoritmo Knuth-Morris-Pratt (KMP) . . . . . . . . . 170
7.2.2. String searching (Boyer-Moore) . . . . . . . . . . . . . 174
7.3. Palı́ndromo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
7.3.1. Longest palindrome (Manacher) . . . . . . . . . . . . . 179
7.4. Hashing de cadenas . . . . . . . . . . . . . . . . . . . . . . . . 181
7.4.1. Cálculo del hash de una cadena . . . . . . . . . . . . . 182
7.4.2. Colisión . . . . . . . . . . . . . . . . . . . . . . . . . . 183
7.4.3. Buscar cadenas duplicadas en un arreglo de cadenas . . 184
7.4.4. Cálculo hash rápido de subcadenas de una cadena dada 185
7.4.5. Determine el número de subcadenas diferentes en una
cadena . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
7.5. Expresiones regulares . . . . . . . . . . . . . . . . . . . . . . . 187
7.6. Longest repeated substring (Karp Miller Rosenberg) . . . . . . 187
7.7. Recursive-descent parsing (LL (1)) . . . . . . . . . . . . . . . 187
7.8. Squares determination . . . . . . . . . . . . . . . . . . . . . . 187
7.9. Z Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
7.10. Multi-pattern search (Aho-Corasick) . . . . . . . . . . . . . . 187
7.11. String search (Shift And) . . . . . . . . . . . . . . . . . . . . . 187
7.12. Two-dimensional string search (Baker-Bird) . . . . . . . . . . 187
7.13. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 187
8. Combinatoria 193
8.1. Variaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
8.1.1. Variación con repetición . . . . . . . . . . . . . . . . . 194
8.2. Permutaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
8.2.1. Permutación con repetición . . . . . . . . . . . . . . . 195
8.3. Combinaciones . . . . . . . . . . . . . . . . . . . . . . . . . . 195
8.3.1. Algunas propiedades de los números combinatorios . . 196
8.4. Máscara de bit . . . . . . . . . . . . . . . . . . . . . . . . . . 200
ÍNDICE GENERAL 7
10.Geometrı́a 243
10.1. Distancia mı́nima en el plano (Closest Pair) . . . . . . . . . . 243
10.1.1. Primera variante de solución: Solución iterativa . . . . 243
10.1.2. Segunda variante de solución: Divide y Conquista . . . 245
10.1.3. Tercera variante de solución: Algoritmo de lı́nea de ba-
rrido . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
10.1.4. Cuarta variante de solución: Algoritmos aleatorios in-
crementales . . . . . . . . . . . . . . . . . . . . . . . . 251
10.2. Distancia esférica . . . . . . . . . . . . . . . . . . . . . . . . . 254
10.3. Cubiertas convexas . . . . . . . . . . . . . . . . . . . . . . . . 254
10.3.1. Algoritmo Incremental . . . . . . . . . . . . . . . . . . 256
10.3.2. La Caminata de Jarvis . . . . . . . . . . . . . . . . . . 257
10.3.3. Algoritmo QuickHull . . . . . . . . . . . . . . . . . . . 259
10.3.4. Algoritmo de Cubierta Convexa aplicando Técnica de
Divide y Vencerás . . . . . . . . . . . . . . . . . . . . . 261
8 ÍNDICE GENERAL
14.Greedy 399
14.0.1. Fundamentos . . . . . . . . . . . . . . . . . . . . . . . 399
14.1. Intervalo de Tareas (Interval Scheduling) . . . . . . . . . . . . 402
14.2. El fontanero diligente . . . . . . . . . . . . . . . . . . . . . . . 403
14.2.1. Mas fontaneros . . . . . . . . . . . . . . . . . . . . . . 405
14.3. Los ficheros y el disquete . . . . . . . . . . . . . . . . . . . . . 405
14.4. El camionero con prisa . . . . . . . . . . . . . . . . . . . . . . 406
14.5. Análisis de ejercicios . . . . . . . . . . . . . . . . . . . . . . . 408
16.Misceláneas 491
16.1. Trabajo con fechas . . . . . . . . . . . . . . . . . . . . . . . . 491
16.1.1. Calendario Juliano . . . . . . . . . . . . . . . . . . . . 491
16.1.2. Calendario Gregoriano . . . . . . . . . . . . . . . . . . 492
16.1.3. Java: Trabajo con fecha . . . . . . . . . . . . . . . . . 492
16.1.4. Dı́a de la semana . . . . . . . . . . . . . . . . . . . . . 497
ÍNDICE GENERAL 13
14
Prólogo
15
PRÓLOGO
res asi que puede encontrar temáticas con solamente el nombre, eso significa
que ya se pensado abordarlo lo que aún no se ha tenido el tiempo.
Tambien fueron utilizados los siguientes jurados online:
MOG Matcom Online Grader http://judge.matcom.uh.cu/
TIMUS Timus Online Judge http://acm.timus.ru
Athens Online Judge http://aoj.umcc.cu
DMOJ http://dmjo.uclv.cu
16
Capı́tulo 1
Lenguajes
1.1. C++
C++ es un lenguaje de programación diseñado a mediados de los años
1980 por Bjarne Stroustrup. La intención de su creación fue el extender al
exitoso lenguaje de programación C con mecanismos que permitan la mani-
pulación de objetos. En ese sentido, desde el punto de vista de los lenguajes
orientados a objetos, el C++ es un lenguaje hı́brido. El nombre C++ fue
propuesto por Rick Mascitti en el año 1983, cuando el lenguaje fue utilizado
por primera vez fuera de un laboratorio cientı́fico. Antes se habı́a usado el
nombre C con clases. En C++, la expresión C++ significa incremento de C
y se refiere a que C++ es una extensión de C.
El 12 de agosto de 2011, Herb Sutter, presidente del comité de estándares
de C++, informó la aprobación unánime del nuevo estándar. Esta nueva
17
LENGUAJES
versión se conoce como C++11. Entre las caracterı́sticas del nuevo estándar
se pueden destacar:
1. Funciones lambda
2. Referencias rvalue
4. Inicialización uniforme
# define PI 3.14
# define LL long long
18
LENGUAJES
LL a ;
int main ()
{
// desarrollo de nuestra solucion
return 0;
}
19
LENGUAJES
int main ()
{
// freopen (" Input . txt " ," r " , stdin ) ;
cout . setf ( ios :: fixed , ios :: floatfield ) ;
cout . precision (0) ;
ios_base :: sync_with_stdio (0) ;
std :: cin . tie (0) ;
while ( cin > > cases )
{
while ( cases - -)
{
cin > > lat_ptoA > > long_ptoA > >
lat_ptoB > > long_ptoB ;
arc = distanceSphere ( lat_ptoA ,
20
LENGUAJES
21
LENGUAJES
int cases ;
cin > > cases ;
while ( cases - -)
{
/* Desarrollo de la solucion */
}
/* otra variante cuando necesito saber los casos */
for ( int i =1; i <= cases ; i ++)
{
/* Desarrollo de la solucion */
}
int entrada2 ;
while ( cin > > entrada2 )
{
/* Desarrollo de la solucion */
}
En ciertos problemas se hace necesario leer toda una lı́nea incluyendo los
espacios para dicha situación se puede utilizar lo siguiente:
string line
22
LENGUAJES
getline(cin,line)
Tener en cuenta que el salto de lı́nea no lo captura y si vuelves a leer otra
cadena seguido solo vas a leer el salto de lı́nea.
23
LENGUAJES
int main ()
{
24
LENGUAJES
C ++ todo el tiempo */
ios_base :: sync_with_stdio (0) ;
std :: cin . tie (0) ;
1.2. Java
El lenguaje de programación Java fue originalmente desarrollado por Ja-
mes Gosling de Sun Microsystems (la cual fue adquirida por la compañı́a
Oracle) y publicado en 1995 como un componente fundamental de la plata-
forma Java de Sun Microsystems. Su sintaxis deriva en gran medida de C y
C++, pero tiene menos utilidades de bajo nivel que cualquiera de ellos. Las
aplicaciones de Java son generalmente compiladas a bytecode (clase Java)
que puede ejecutarse en cualquier máquina virtual Java (JVM) sin importar
la arquitectura de la computadora subyacente.
Debido a su mecanismo de compilación la soluciones realizadas Java tiene
un consumo de tiempo y memoria mayor que las hechas en C++ con similar
algoritmo es por eso que en muchos jurados a las soluciones que se envı́a en
Java se le amplia las restricciones de tiempo y memoria tres veces con respec-
to a las restricciones hechas para soluciones con C++. Esto no significa que
una solución hecha con Java siempre sea su velocidad y consumo de memoria
tres veces superior a la misma implementación pero con C++. Esto puede
un arma que podemos usar a nuestro favor en determinadas situaciones. Si
tenemos una solución con cuyo resultado es tiempo lı́mite excedido (TLE)
porque su tiempo de ejecución fue superior por muy poco al lı́mite tiempo
impuesto para una solución en C++, bien podemos tratar de codificar la
misma solución de C++ con Java. Pueden pensar que volverá a dar como
resultado TLE y no se equivocan puede ser ese el resultado, ya lo sabiamos
por los intentos con C++. Pero como dicen que la matemática es una ciencia
25
LENGUAJES
26
LENGUAJES
Nombre de clase.metodo(<argumentos>)
2
Un token o también llamado componente léxico es una cadena de caracteres que tiene
un significado coherente en cierto lenguaje de programación. Ejemplos de tokens podrı́an
ser palabras clave (if, else, while, int, ...), identificadores, números, signos, o un operador
de varios caracteres, (por ejemplo, :=).
27
LENGUAJES
Debido a que para la captura de datos Java ofrece dos variantes aqui les
dejo dos posibles pantilla que pueden untilizar cada una con una variante
distintas de lectura de datos. La primera utiliza para la captura de datos la
clase Scanner.
private Scanner in ;
private PrintWriter out ;
private double ERROR = 1e -15;
28
LENGUAJES
29
LENGUAJES
30
LENGUAJES
}
eat ( line ) ;
}
return st . nextToken () ;
}
31
LENGUAJES
32
LENGUAJES
1.3.1. C++
33
LENGUAJES
1.3.2. Java
34
LENGUAJES
1.4.1. Entrada
C++
Para capturar los datos en el lenguaje de programación C++ se puede
utilizar dos variantes. La primera es la que propone su lenguaje de programa-
ción antecesor C y la segunda es la que propone el lenguaje de programación
35
LENGUAJES
en sı́.
A diferencia de otros lenguajes, C no dispone de sentencias de entrada/-
salida. En su lugar se utilizan funciones contenidas en la librerı́a estándar y
que forman parte integrante del lenguaje.
Las funciones de entrada/salida (Input/Output) son un conjunto de fun-
ciones, incluidas con el compilador, que permiten a un programa recibir y
enviar datos al exterior. Para su utilización es necesario incluir, al comienzo
del programa, el archivo stdio.h en el que están definidos sus prototipos:
int scanf ( " %x1 %x2 ... " , & arg1 , & arg2 , ...) ;
donde x1, x2, ... son los caracteres de conversión, mostrados en la tabla,
que representan los formatos con los que se espera encontrar los datos. La
función scanf() devuelve como valor de retorno el número de conversiones de
formato realizadas con éxito. La cadena de control de scanf() puede contener
caracteres además de formatos. Dichos caracteres se utilizan para tratar de
detectar la presencia de caracteres idénticos en la entrada por teclado. Si lo
que se desea es leer variables numéricas, esta posibilidad tiene escaso interés.
A veces hay que comenzar la cadena de control con un espacio en blanco
para que la conversión de formatos se realice correctamente.
En la función scanf() los argumentos que siguen a la cadena de control
deben ser pasados por referencia, ya que la función los lee y tiene que tras-
mitirlos al programa que la ha llamado. Para ello, dichos argumentos deben
estar constituidos por las direcciones de las variables en las que hay que depo-
sitar los datos, y no por las propias variables. Una excepción son las cadenas
de caracteres, cuyo nombre es ya de por sı́ una dirección (un puntero), y por
tanto no debe ir precedido por el operador (&) en la llamada.
Por ejemplo, para leer los valores de dos variables int y double y de una
cadena de caracteres, se utilizarı́an la sentencia:
int n ;
double distancia ;
char nombre [20];
scanf ( " %d %lf %s " , &n , & distancia , nombre ) ;
36
LENGUAJES
lee todos los caracteres que encuentra hasta que llega al carácter nueva
lı́nea ’
n’. Esta sentencia puede utilizarse por tanto para leer lı́neas completas, con
blancos incluidos. Recuérdese que con el formato %s la lectura se detiene al
llegar al primer delimitador (carácter blanco, tabulador o nueva lı́nea)
37
LENGUAJES
c = getchar () ;
equivale a
int i =0 , c ;
char name [100];
while (( c = getchar () ) != ’\ n ’) // se leen caracteres
hasta el ’\ n ’
name [ i ++] = c ; // se almacena el
caracter en Name []
name [ i ]= ’ \0 ’; // se adiciona el
caracter fin de cadena
38
LENGUAJES
es utilizada por programadores expertos, y otra de alto nivel, con las clases:
istream, ostream e iostream, que derivan de la clase ios. Estas clases dispo-
nen de variables y métodos para controlar los flujos de entrada y salida. En
nuestro caso utilizaremos la clase iostream de la siguiente manera.
Del mismo modo que puede sobrecargarse el operador de salida <<, pue-
de sobrecargarse el operador de entrada >> . En C++ , el operador >> se
denomina operador de extracción y la función que lo sobrecarga se denomina
extractor. La razón del uso de este término está en el hecho de que la intro-
ducción de información en un flujo elimina (esto es, extrae) los datos de ella.
La forma general de una función extractora es:
istream & operator > > ( istream & stream , class - name & obj )
{
// Instrucciones de para la lectura
return stream ;
}
que permiten la desincronización del cin, esto es para que el cin tenga la
misma velocidad del scanf de C, pero si usa esto no puede combinar cin/cout
con scanf/printf o usa C todo el tiempo o usa C++ todo el tiempo, nunca
un hibrido.
39
LENGUAJES
Java
En Java, la entrada desde teclado y la salida a pantalla están reguladas
a traves de la clase System. Esta clase pertenece al package java.lang y
agrupa diversos métodos y objetos que tienen relación con el sistema local.
Contiene, entre otros, tres objetos static que son:
char c ;
c =( char ) System . in . read () ;
Para leer datos más largos que un simple carácter es necesario emplear
un bucle while o for y unir los caracteres. Por ejemplo, para leer una lı́nea
completa se podrı́a utilizar un bucle while guardando los caracteres leı́dos en
un String o en un StringBuffer (más rápido que String):
40
LENGUAJES
char c ;
String frase = new String ( " " ) ; // StringBuffer frase = new
StringBuffer ("") ;
while (( c = System . in . read () ) != ’\ n ’)
frase = frase + c ; // frase . append ( c ) ;
Una vez que se lee una lı́nea, ésta puede contener números de coma flo-
tante, etc. Sin embargo, hay una manera más fácil de conseguir lo mismo:
utilizar adecuadamente la librerı́a java.io.
Para facilitar la lectura de teclado se puede conseguir que se lea una lı́nea
entera con una sola orden si se utiliza un objeto BufferedReader. El método
String readLine() perteneciente a BufferReader lee todos los caracteres hasta
encontrar un ’\n’ o ’\r’ y los devuelve como un String (sin incluir ’\n’ ni
’\r’). Este método también puede lanzar java.io.IOException.
Como se mencionó antes BufferedReader es una clase cuyas instancias nos
permiten hacer lecturas sencillas de texto desde un flujo de caracteres, debido
a que esta clase trabaja con su propio buffer provee una lectura eficiente de
caracteres, arreglos y lı́neas de texto. Otra ventaja de BufferedReader es que
esta clase está sincronizada lo cual es sinónimo de seguridad al utilizarla en
programación concurrente.
Primero que todo, esta clase se ubica en el paquete java.io por lo que en
los códigos donde se utilice BufferedReader es necesario poner los siguientes
import:
41
LENGUAJES
Ası́ ya se ha leido una lı́nea del teclado. El thread que ejecute este códi-
go estará parado en esta lı́nea hasta que el usuario termine la lı́nea (pulse
return). Es más sencillo y práctico que la posibilidad anterior.
¿Y qué hacer con una lı́nea entera? La clase java.util.StringTokenizer da
la posibilidad de separar una cadena de carácteres en las “palabras” (tokens)
que la forman (por defecto, conjuntos de caracteres separados por un espacio,
’\t’, ’\r’, o por ’\n’). Cuando sea preciso se pueden convertir las “palabras”
en números.
La siguiente tabla muestra los métodos más prácticos de la clase String-
Tokenizer.
42
LENGUAJES
int n ;
System . out . print ( " Introduzca un numero entero : " ) ;
n = sc . nextInt () ;
Método Descripción
nextXxx() Devuelve el siguiente token como un tipo básico. Xxx es el tipo.
Por ejemplo, nextInt() para leer un entero, nextDouble para leer
un double, etc.
next() Devuelve el siguiente token como un String.
43
LENGUAJES
1.4.2. Salida
C++
La función printf() imprime en la unidad de salida (el monitor, por
defecto), el texto, y las constantes y variables que se indiquen. La forma
general de esta función se puede estudiar viendo su prototipo:
int printf ( " c adena_ de_con trol " , tipo arg1 , tipo arg2 ,
...)
int i ;
double tiempo ;
float masa ;
printf ( " Resultado no : %d . En el instante %lf la masa
vale %f \ n " ,i , tiempo , masa ) ;
44
LENGUAJES
El resultado serán dos lı́neas con las dos primeras estrofas de la famosa
poesı́a. No es posible partir cadena de control en varias lı́neas con caracte-
res intro, por lo que en este ejemplo podrı́a haber problemas para añadir más
estrofas. Una forma alternativa, muy sencilla, clara y ordenada, de escribir
la poesı́a serı́a la siguiente:
45
LENGUAJES
printf ( " En el dia %s gano %ld ptas .\ n " , " 24 " , beneficios
);
enum {
skipws =0 x0001 , left =0 x0002 , rigth =0 x0004 ,
internal =0 x0008 ,
dec =0 x0010 , oct =0 x0020 , hex =0 x0040 ,
showbase =0 x0080 ,
showpoint = 0 x0100 , seupperca =0 x0200 , showpos =0 x0400 ,
scientific =0 x800 ,
fixed =0 x1000 , unitbuf =0 x2000
};
Su significado es el siguiente:
46
LENGUAJES
47
LENGUAJES
que lo que hace es desactivar los tres bits que tienen que ver con la
alineación y después activar el bit de alineación por la izquierda. En la forma,
long flags () ;
48
LENGUAJES
+100.000000
ostream & operator < < ( ostream & co , const obj_type & a ) ;
49
LENGUAJES
Este flujo funciona como cinta transportadora que sale (<<) del progra-
ma. Se recibe una referencia al flujo como primer argumento, se añade o se
retira de él la variable que se desee, y se devuelve como valor de retorno una
referencia al flujo modificado. El valor de retorno es siempre una referencia
al stream de entrada/salida correspondiente. A continuación se presenta un
ejemplo de sobrecarga del operador (<<):
ostream & operator < <( ostream & co , const matriz & A )
{
for ( int i =0; i <= nfilas ; i ++)
for ( int j =0; j <= ncol ; j ++)
co << A . c [ i ][ j ] << ’\ t ’;
co << ’\ n ’;
return ( co ) ;
}
Java
Para imprimir en la pantalla se utilizan los métodos System.out.print()
y System.out.println(). Son los primeros métodos que aprende cualquier
programador. Sus caracterı́sticas fundamentales son:
1. Pueden imprimir valores escritos directamente en el código o cualquier
tipo de variable primitiva de Java.
50
LENGUAJES
1.504582 x 102
1.504582e+02
3
Java SE 5 tomó prestada esta caracterı́stica del lenguaje de programación C
51
LENGUAJES
Carácter de Descripción
conversión
eoE Muestra un valor de punto flotante en notación ex-
ponencial. Cuando se utiliza el carácter de conver-
sión E , la salida se muestra en letras mayúsculas.
f Muestra un valor de punto flotante en formato de-
cimal.
goG Muestra un valor de punto flotante en el formato
de punto flotante f o en el formato exponencial e ,
con base en la magnitud del valor. Si la magnitud
es menor que 10−3 ,o si es mayor o igual que 107 , el
valor de punto flotante se imprime con e (o E ). En
cualquier otro caso, el valor se imprime en el for-
mato f. Cuando se utiliza el carácter de conversión
G , la salida se muestra en letras mayúsculas.
aoA Muestra un número de punto flotante en formato
hexadecimal. Cuando se usa el carácter de conver-
sión A , la salida se muestra en letras mayúsculas.
Los valores que se imprimen con los caracteres de conversión e , E y
f se muestran con seis dı́gitos de precisión en el lado derecho del pun-
to decimal de manera predeterminada (por ejemplo, 1.045921 ); otras
precisiones se deben especificar de manera explı́cita. Para los valores
impresos con el carácter de conversión g , la precisión representa el
número total de dı́gitos mostrados, excluyendo el exponente. El valor
predeterminado es de seis dı́gitos (por ejemplo, 12345678.9 se muestra
como 1.23457e+07 ). El carácter de conversión f siempre imprime por
lo menos un dı́gito a la izquierda del punto decimal. Los caracteres de
conversión e y E imprimen una e minúscula y una E mayúscula an-
tes del exponente, y siempre imprimen sólo un dı́gito a la izquierda
del punto decimal. El redondeo ocurre si el valor al que se está dando
formato tiene más dı́gitos significativos que la precisió
El carácter de conversión g (o G ) imprime en formato e ( E ) o f , depen-
diendo del valor de punto flotante. Por ejemplo, los valores 0.0000875
, 87500000.0 , 8.75 , 87.50 y 875.0 se imprimen como 8.750000e-05 ,
8.750000e+07 , 8.750000 , 87.500000 y 875.000000 con el carácter de
conversión g . El valor 0.0000875 utiliza la notación e ya que la mag-
nitud es menor que 10−3 . El valor 87500000.0 utiliza la notación e ,
debido a que la magnitud es mayor que 107 .
52
LENGUAJES
e +07
System . out . printf ( " %e \ n " , +12345678.9 ) ; //
1.234568 e +07
System . out . printf ( " %e \ n " , -12345678.9 ) ; //
-1.234568 e +07
System . out . printf ( " %E \ n " , 12345678.9 ) ; // 1.234568
E +07
System . out . printf ( " %f \ n " , 12345678.9 ) ; //
12345678.900000
System . out . printf ( " %g \ n " , 12345678.9 ) ; // 1.23457 e
+07
System . out . printf ( " %G \ n " , 12345678.9 ) ; // 1.23457 E
+07
53
LENGUAJES
54
LENGUAJES
Carácter de Descripción
conversión
boB Imprime true o false para el valor de un boolean
o Boolean . Estos caracteres de conversión tam-
bién pueden aplicar formato al valor de cualquier
referencia. Si la referencia no es null, se imprime
true ; en caso contrario, se imprime false . Cuando
se utiliza el carácter de conversión B , la salida se
muestra en letras mayúsculas.
hoH Imprime la representación de cadena del valor de
código hash de un objeto en formato hexadecimal.
Si el correspondiente argumento es null , se impri-
me null . Cuando se utiliza el carácter de conver-
sión H , la salida se muestra en letras mayúsculas.
% Imprime el carácter de por ciento.
n Imprime el separador de lı́nea especı́fico de la pla-
taforma (por ejemplo, \r\n en Windows o \n en
UNIX/LINUX).
// Ot rasCon versio nes . java
// Uso de los caracteres de conversion b , B , h , H , %
y n.
public class Ot rasCon versio nes
{
public static void main ( String args [] )
{
Object prueba = null ;
System . out . printf ( " %b \ n " , false ) ;
System . out . printf ( " %b \ n " , true ) ;
System . out . printf ( " %b \ n " , " Prueba " ) ;
System . out . printf ( " %B \ n " , prueba ) ;
System . out . printf ( " El codigo hash de \" hola \" es %h
\ n " , " hola " ) ;
System . out . printf ( " El codigo hash de \" Hola \" es %h
\ n " , " Hola " ) ;
System . out . printf ( " El codigo hash de null es %H \ n " ,
prueba ) ;
System . out . printf ( " Impresion de un % % en una cadena
de formato \ n " ) ;
System . out . printf ( " Impresion de una nueva linea %
nla siguiente linea empieza
aqui " ) ;
} // fin de main
55
LENGUAJES
56
LENGUAJES
la cual muestra 123.457 con tres dı́gitos a la derecha del punto decimal,
y se justifi ca a la derecha en un campo de nueve dı́gitos; antes del
número se colocarán dos espacios en blanco en su campo.
57
LENGUAJES
Bandera Decripción
- (signo nega- Justifica a la izquierda la salida dentro del campo
tivo) especificado.
+ (signo posi- Muestra un signo positivo antes de los valores po-
tivo) sitivos, y un signo negativo antes de los valores
negativos.
espacio Imprime un espacio antes de un valor positivo que
no se imprime con la bandera + .
] Antepone un 0 al valor de salida cuando se utiliza
con el carácter de conversión octal o .Antepone 0x
al valor de salida cuando se usa con el carácter de
conversión hexadecimal x .
0 (cero) Rellena un campo con ceros a la izquierda.
, (coma) Usa el separador de miles especı́fico para la confi-
guración regional (es decir, ’ , ’ para los EUA), para
mostrar números decimales y de punto flotante.
( Encierra los números negativos entre paréntesis.
58
LENGUAJES
1.5. Operadores
Tanto C++ como Java están llenos de operadores que junto con las varia-
bles conforman las expresiones o sentencias que a su vez integran un programa
o algoritmo a continuación mencionaremos algunos de los más útiles.
59
LENGUAJES
60
LENGUAJES
61
LENGUAJES
9 %3==0 ? cout < < " El 3 divide al 9. " << endl : cout < < " El 3
no divide al 9. " << endl ;
Sentencia if
Esta sentencia de control permite ejecutar o no una sentencia simple o
compuesta según se cumpla o no una determinada condición. Esta sentencia
tiene la siguiente forma general:
if ( expresion ) {
sentencia ;
}
int a , b ;
cin > >a > > b ;
if (a > b ) {
cout < < " El valor de la variable a es mayor que el valor
de la variable b . " << endl ;
}
if ( expresion ) {
sentencia_1 ;
} else {
sentencia_2 ;
}
62
LENGUAJES
int a , b ;
cin > >a > > b ;
if (a > b ) {
cout < < " El valor de la variable a es mayor que el valor
de la variable b . " << endl ;
} else {
cout < < " El valor de la variable a es menor o igual que
el valor de la variable b . " << endl ;
}
if ( expresion_1 )
sentencia_1 ;
else if ( expresion_2 )
sentencia_2 ;
else if ( expresion_3 )
sentencia_3 ;
else if (...)
...
[ else
sentencia_n ;]
int a , b ;
63
LENGUAJES
Sentencia switch
La sentencia que se va a describir a continuación desarrolla una función
similar a la de la sentencia if ... else con múltiples ramificaciones, aunque como
se puede ver presenta también importantes diferencias. La forma general de
la sentencia switch es la siguiente:
switch ( expresion ) {
case expresion_cte_1 :
sentencia_1 ;
break ;
case expresion_cte_2 :
sentencia_2 ;
break ;
...
case expresion_cte_n :
sentencia_n ;
break ;
[ default :
sentencia ;]
}
64
LENGUAJES
int a ;
cin > > a ;
switch ( a ) {
case 0:
cout < < " El valor de a es 0. " ;
break ;
case 1:
cout < < " El valor de a es 1. " ;
break ;
default :
cout < < " El valor de a no es ni 1 ni 0. " ;;
}
Sentencias if anidadas
Una sentencia if puede incluir otros if dentro de la parte correspondiente
a su sentencia, A estas sentencias se les llama sentencias anidadas (una dentro
de otra), por ejemplo,
if (a >= b )
if ( b !=0.0)
c=a/b;
if (a >= b )
if ( b !=0.0)
c=a/b;
else
c =0.0;
65
LENGUAJES
if (a >= b ) {
if ( b !=0.0)
c=a/b;
} else
c =0.0;
Sentencia while
Esta sentencia permite ejecutar repetidamente, mientras se cumpla una
determinada condición, una sentencia o bloque de sentencias. La forma ge-
neral es como sigue:
while ( e x p r e s i o n _ d e _ c o n t r o l ) {
sentencia ;
}
66
LENGUAJES
int a ;
cin > > a ;
cout < < " Haciendo conteo regresivo " << endl ;
while (a >0) {
cout < < endl ;
a =a -1;
}
Sentencia for
For es quizás el tipo de bucle mas versátil y utilizado en los lenguajes de
programación. Su forma general es la siguiente:
inicializacion ;
while ( e x p r e s i o n _ d e _ c o n t r o l ) {
sentencia ;
actualizacion ;
}
donde sentencia puede ser una única sentencia terminada con (;), otra
sentencia de control ocupando varias lı́neas (if, while, for, ...), o una senten-
cia compuesta o un bloque encerrado entre llaves .... Antes de iniciarse el
bucle se ejecuta inicializacion, que es una o más sentencias que asignan valo-
res iniciales a ciertas variables o contadores. A continuación se evalúa expre-
sion de control y si es false se prosigue en la sentencia siguiente a la construc-
ción for; si es true se ejecutan sentencia y actualizacion, y se vuelve a evaluar
expresion de control. El proceso prosigue hasta que expresion de control sea
67
LENGUAJES
do {
sentencia ;
} while ( e x p r e s i o n _ d e _ c o n t r o l ) ;
68
LENGUAJES
sentencias ...
...
if ( condicion )
goto otro_lugar ; // salto al lugar indicado por la
etiqueta
sentencia_1 ;
sentencia_2 ;
...
otro_lugar : // esta es la sentencia a la que se salta
sentencia_3 ;
...
69
LENGUAJES
Para este caso la captura del dato de entrada serı́a de la siguiente manera
dependiendo del lenguaje:
C++ Java
Formato de entrada:
70
LENGUAJES
C++ Java
sKe = in . nextInt () ;
sMKe = in . nextInt () ;
gPKe = in . nextInt () ;
sS = in . nextInt () ;
sMS = in . nextInt () ;
gPS = in . nextInt () ;
// Resto de la solucion
}
}
71
LENGUAJES
72
Capı́tulo 2
Add Hoc
Unas de las temáticas dentro de las cuales pueden ser agrupados los pro-
blemas de concurso de programación es Add Hoc. Esta temática a diferencia
de otras donde existen tanto problemas como algoritmos clásicos bien defi-
nidos, no cuenta ni con uno ni con otro. Los problemas que son agrupados
en esta temática sus soluciones dependen en gran medida de las habilida-
des y creatividad de los programadores. Por lo general son los problemas
clasificados en esta temática los que comienzan hacer los concursantes de
programación cuando se inician, primero porque en su gran mayorı́a la solu-
ción radica en seguir algún procedimiento descripto en el propio problema,
segundo porque no se requiere un gran conocimiento en determinadas áreas
del conocimiento como pueden ser teorı́a de número, de grafos ,ect. Solo re-
quiere determinado nivel o habilidad sobre algún lenguaje de programación.
Esto no significa que sean los ejercicios más sencillos de resolver la mayoria
de los casos es ası́ pero hay algunos que se la traen.
Pues bien como los problemas de tipo Add Hoc no tienen problemas y
algoritmos clásicos que debamos saber para enfrentarlos, vamos aprovechar
esta sección para abordar algunos aspectos que son importantes no solo pa-
ra resolver los problemas enmarcados dentro de la temática sino cualquier
problema de concurso de programación :
Lo primero que se debe es leer bien el problema. Muchos se preguntan
porque leer el problema si existe una alta posibilidad de que no lo resuelva,
sobre todo si soy un notavo en este mundillo. Es un tremendo error pensar
de esta manera. Primero el no leer bien nos puede provocar omitir detalles
importante a la hora de generar una solución. Segundo, con una buena lec-
tura la compresión se hace mejor, y podemos tener un mejor criterio para
decidir si con los conocimientos propios y las restricciones del problema po-
demos desarrollar una soución que se ajuste. Por último le podemos dar una
verdadera clasificación al problema según nuestro criterio. Los grupos en los
73
ADD HOC
74
ADD HOC
3682 - Triangle Quality Ejercicio sencillo que solo se debe hacer lo que
plantea el enunciado del problema.
3073 - Way Too Long Words Para solucionar este problema solo se
debe leer la cadena si la longitud es 10 o menor se imprime la cadena sino se
imprime el primer sı́mbolo de la cadena , la longitud de la cadena menos dos
y el último caracter de la cadena.
75
ADD HOC
3770 - Median of letters Para solucionar este problema solo debe ordenar
alfabeticamente de manera ascendente la cadena que dan como entrada e
imprimir la letra que se encuentra en la posición longitud/2 de la cadena
ordenada.
76
ADD HOC
3103 - Darts Este ejercicio solo require simular el proceso que describen,
tener una función que devuelva la distancia de una cooredenada determinada
con respecto al centro de la diana y otra que dada una distancia devuelva la
cantidad de puntos que le corresponden. Tener en cuenta que si una dardo
cae sobre una de las circunferencia divisoras de la diana, se puntea el disparo
por el circulo menor.
3742 - Anders And The Names La solución del ejercicio solo radica
en leer toda una linea. De esa linea debemos contar la cantidad de palabras
y la cantidad de letras en mayúsculas. Si dichas cantidades son iguales la
respuesta es correct en caso contrario la repuesta es awful
3827 - Tuna Ejercicio sencillo del cual solo debemos implementar un al-
goritmo que siga el procedimiento descrito en el problema.
1099 - Pythagorean Numbers El ejercicio solo nos pide dada una triada
de números chequear si dichos números conforman una terna pitagórica. EL
detalle del problema es que se deben verificar todas las combinaciones posibles
(a2 + b2 == c2 , a2 + c2 == b2 ,c2 + b2 == a2 ), si al menos una de ellas es
verdadera , la respuesta será right sino la respuesta será wrong.
3869 - Elevator Task Para resolver el problema solo basta con implemen-
tar un algoritmo que resuelva la siguiente ecuación S=A+B+C donde:
A= |1 − a1 |
B= |a1 − 1|
Pi=1
C= n−1 |ai − ai+1 |
77
ADD HOC
1065 - Money Matters A pesar que el ejercicio esta catalogado como Ad-
Hoc 2. Para solucionarlo me apoye en teorı́a de grafos. Construı́ el grafo donde
los nodos son las personas y las aristas las amistades. Una vez modelado
el problema determine la cantidad de componente conexa del grafo y para
cada componente sume las deudas y el dinero de sobra de cada nodo de esa
componente. Si alguna suma de las componentes da menor que cero entonces
la respuesta es IMPOSIBLE sino POSIBLE.
78
ADD HOC
1327 - Barbecue Una vez leı́do el problema es claro entender que debe-
mos hallar cual es el miembro de la familia vamos a dejar sin carne. Para
hallarlo vamos a crear un estructura que bien se puede llamar Person la cual
va contener tres atributos de tipo entero: id , cantidad de votos recibidos,
cantidad de votos emitidos. Luego con un arreglo de esta estructura donde
cada posición va representar un miembro de la familia vamos actualizar el
valor de cada variable de los miembros de la famila según los datos de en-
tradas. Una vez actualizado los datos, ordenamos el arreglo de acuerdo a la
reglas para definir quien se debe quedar sin carne y la respuesta al ejercicio
quedará o bien en la ultima posición del arreglo o en la primera de acuerdo
a como se haya ordenado. Se debe verificar que ninguna persona vote más
de una vez por una misma persona o por ella misma elemento que se puede
controlar con una matriz booleana.
2465 - Adding Hex Numbers El problema nos pide dado dos núme-
ros representados en base hexadecimal determinar la cantidad acarreos que
produce la suma entre estos. La solución es bastante sencilla, solo basta im-
plementar un algoritmo que simule el proceso de suma y que nos permita
llevar la cuenta de la cantidad de acarreos que esta genera. Se debe tener en
cuenta los números no tienen que tener la misma cantidad de digitos.
79
ADD HOC
2207 - Grid Rotation El problema nos pide determinar dada una matriz
A cuadrada de dimension n y una colección de matrices saber si cada matriz
de la colección la matriz A se puede transformar en ella aplicando una o
varias rotaciones de 90 grados lo mismo en sentido de las manecillas del
reloj o contraria. Para solucionar el problema se puede crear tres matrices
auxiliares en las cuales vamos a guardar como va quedar la matriz A con
un giro de 90, 180 y 270, luego para cada matriz de la colección basta con
probar que esta sea igual a una de las cuatro posibles transformaciones de la
matriz A para que la respuesta sea YES y NO en caso contrario. Sugiero que
tanto la matriz A como sus transformaciones sean alamcenadas en variables
de tipo cadena. De igual forma cada matriz a analizar se debe guardar en
variable de tipo cadena para ser mas fácil la comparación.
80
ADD HOC
4062 - Help a PhD candidate out! El ejercicio nos pide leer una serie de
entradas y para cada entrada que sea del tipo ’a + b’ donde a y b son números
imprimir la suma de estos. En caso de que la entrada sea del tipo ’P=NP’ la
salida para esa entrada debe ser ’skipped’. La complejidad es bastante baja
quiza es punto de mayor complejidad sea el análisis de la cadena entrada en
cada caso, lo cual puede ser complicado o no en dependencia del lenguajes
de programación que utilices.
81
ADD HOC
4081 - Almost Accepted Dada dos cadenas. Donde la primera hace re-
ferencia a una salida correcta de un problema determinado y la segunda es
la salida que da una solución que se le hizo al problema. Determinar si dicha
solución es Accepted, Wrong Answer o Presentation Error.
Para ver si es correcta se debe cumplir que:
82
ADD HOC
3946 - Counting vowels Una vez leido el problema nos podemos percatar
que para una secuencia de caracteres de longitud N nos piden hallar cuantas
vocales hay en el primer subgrupo de caracteres de la cadena que son los
primeros N/3+1 caracteres , cuantas vocales hay en el segundo subgrupo de
caracteres de la cadena que son los segundos N/3+1 caracteres y cuantas
vocales existen en el tercer subgrupo que son los caracteres sobrantes de la
cadena que no estan ni en el primer y segundo subgrupo.
4124 - Chuqui and his little ball Una vez leı́do el problema nos po-
demos percatar que nos pide hallar la mayor longitud de subsecuencia de
elementos concecutivos de la secuencia de entrada cuyo valores en los extre-
mos sean iguales. Para resolver dicho problema vamos ver que los valores de
la secuencia están en el rango de 1 a 106 , este dato nos permitará implemen-
tar un algoritmo con una idea similar al algoritmo de ordenamiento similar
al CountingSort. Vamos a tener un arreglo llamado indexs cuya dimensión va
ser de 106 e inicialmente cada posición con un valor -1 y una variable range
que va guardar el resultado final cuyo valor inicial es 1 . Una vez hecho esto
vamos a empezar a leer cada uno de los números que componen ls secuencia
realizando el siguiente procedimiento:
83
ADD HOC
4101 - Indecisos Una vez leı́do el problema podemos analizar que una ley
solo va ser aprobada si la cantidad de legisladores que van votar por el NO
mas la cantidad de legisladores que están indecisos no superan o igualan la
cantidad de legisladores que votarán por el SI. Forma inversa la ley no va
ser aprobada si la cantidad de legisladores que van a votar por el SI mas
la cantidad de legisladores que están indecisos no superan la cantidad de
legisladores que votarán por el NO. En cualquier otro caso el resultado es
INDECISOS.
84
ADD HOC
85
ADD HOC
86
Capı́tulo 3
Fuerza Bruta
3.1. Complejidad
Una vez dispongamos de un algoritmo que funciona correctamente, es
necesario definir criterios para medir su rendimiento o comportamiento. Estos
criterios se centran principalmente en su simplicidad y en el uso eficiente de
los recursos.
A menudo se piensa que un algoritmo sencillo no es muy eficiente. Sin
embargo, la sencillez es una caracterı́stica muy interesante a la hora de diseñar
un algoritmo, pues facilita su verificación, el estudio de su eficiencia y su
mantenimiento. De ahı́ que muchas veces prime la simplicidad y legibilidad
87
FUERZA BRUTA
Ambas medidas son importantes puesto que, si bien la primera nos ofrece
estimaciones del comportamiento de los algoritmos de forma independiente
del ordenador en donde serán implementados y sin necesidad de ejecutarlos,
la segunda representa las medidas reales del comportamiento del algoritmo.
Estas medidas son funciones temporales de los datos de entrada.
Entendemos por tamaño de la entrada el número de componentes sobre
los que se va a ejecutar el algoritmo. Por ejemplo, la dimensión del vector a
ordenar o el tamaño de las matrices a multiplicar.
La unidad de tiempo a la que debe hacer referencia estas medidas de
eficiencia no puede ser expresada en segundos o en otra unidad de tiempo
concreta, pues no existe un ordenador estándar al que puedan hacer referen-
cia todas las medidas. Denotaremos por T(n) el tiempo de ejecución de un
algoritmo para una entrada de tamaño n.
También es importante hacer notar que el comportamiento de un algo-
ritmo puede cambiar notablemente para diferentes entradas (por ejemplo, lo
ordenados que se encuentren ya los datos a ordenar). De hecho, para muchos
88
FUERZA BRUTA
89
FUERZA BRUTA
90
FUERZA BRUTA
91
FUERZA BRUTA
que solucione el problema, lo pruebe para cada posible valor de entrada (en
este caso solo son cinco posibles entradas) y guarde los resultados. A la hora
de generar la solución que se va enviar se crea una estructura de datos donde
la clave sea la posible entrada y el valor sea el resultado previamente calcula-
do para esa entrada. Este tipo de solución a veces en compentencias de alto
nivel son rechazadas. Pero pienselo por un momento. Tengo un problema que
no puedo resolverlo algoritmicamente por las restricciones que me imponen
el problema pero si puedo crear un algoritmo que me de la solución para cada
posible valor de entrada en un tiempo menor que el tiempo que dispongo (si
esta en una compentencia es casi siempre 4 horas) y mayor que el tiempo de
ejecucción que el tiempo del problema. Entonces puedo implementar un pro-
grama con mi solución, lo ejecuto, me concentro en otro problema y despues
de pasado un tiempo reviso los resultados y conformo una solución que se
ajuste al problema con las posibles entradas y su posibles resultados.
3782 - Cacho Ejercicio muy sencillo que solo debemos verificar si los cinco
numeros que nos dan en cada entrada cumplen la condición descrita en el
problema. De cumplirla la solución es Y y N en caso contrario.
3937 - Flooded Area El ejercicio nos pide determinar dada una matriz
de puntos y asteriscos determinar si los asteriscos existentes conforman un
solo cuadrado. La solución de ejercicio radica en buscar las filas y columnas
extremas que tengan asteriscos, es decir buscar la fila mas arriba y la fila
mas abajo y las columnas mas a la izquierda y mas a la derecha que tenga
asteriscos, de igual forma llevar un contador con la cantidad de asteriscos
encontrados. Una vez terminado la búsqueda y conteo, tenemos que el ancho
del supuesto rectángulo que encierra a todos asteriscos de la matriz es igual
a columnader -columnaizq +1, mientras el alto serı́a filaabajo -filaarriba +1. Con
esto basta con probar que la cantidad de asteriscos sea mayor que cero y que
92
FUERZA BRUTA
1843 - Everyone out of the Pool Dado un rango definido por (a,b)
buscar cuantos números en ese rango cumplen las siguientes restricciones
a<x<b y a<x+1 < b . Donde x es el número buscado en el rango.
El valor x debe poder expresarse como:
x = A∗(A+1)
2
Donde A pertenece a los números naturales.
Además x+1 debe poder expresarse como: x + 1 = B 2
Donde B pertenece a los números naturales.
Una posible solución es generar todos los números triangulares hasta 109
y almacenar aquellos que cuando se incrementan en uno son cuadrados per-
fectos. Luego para cada rango de (a,b) ver cuantos de los números hallados
cumplen con la restricción. La búsqueda se puede hacer con fuerza bruta
porque del rango de 0 a 109 solo hay 13 números que cumplen con las res-
tricciones iniciales del problema.
93
FUERZA BRUTA
3119 - Anders And The Matrix Una vez analizado el problema nos
podemos percatar que nos están pidiendo ver si con una serie de números
podemos formar una matrix cuadrada tal que la suma de los elementos que
integran cada columna sea igual a determinados valores, la suma de los ele-
mentos que integran cada fila sea igual a determinados valores y de igual
manera con la suma de los elementos que integran las diagonales. Como la
máxima dimensión de la matriz es 3 lo que son 9 elementos podemos perfec-
tamente permutar los ı́ndices de los valores (no lo valores porque no aseguran
que sean únicos) y ver si al menos con una permutación generada se puede
formar la matrix que cumpla con las condiciones impuesta. En caso de existir
dicha matrix la respuesta es Yes sino No.
94
Capı́tulo 4
Aritmética y Álgebra
95
ARITMÉTICA Y ÁLGEBRA
96
ARITMÉTICA Y ÁLGEBRA
}
return c ;
}
int main ()
{
lld ** matA ;
matA = new lld *[2];
for ( int i =0; i <2; i ++) matA [ i ] = new lld [3];
matA [0][0] = 1;
matA [0][1] = 2;
matA [0][2] = 3;
matA [1][0] = 4;
matA [1][1] = 5;
matA [1][2] = 6;
lld ** matB ;
matB = new lld *[3];
for ( int i =0; i <3; i ++) matB [ i ] = new lld [2];
matB [0][0] = 7;
matB [0][1] = 8;
matB [1][0] = 9;
matB [1][1] = 10;
matB [2][0] = 11;
matB [2][1] = 12;
return 0;
}
97
ARITMÉTICA Y ÁLGEBRA
98
ARITMÉTICA Y ÁLGEBRA
99
ARITMÉTICA Y ÁLGEBRA
Para la explicación de este algoritmo vamos asumir una función del primer
tipo y nuestro problema en como encontrar el máximo f(x) para el intervalo
[l;r]. Una vez descrita la solución para este caso para el segundo caso la
solución es completamente simétrica.
Considere 2 puntos m1 y m2 en este intervalo:l < m1 < m2 < r. Eva-
luamos la función en m1 y m2, es decir, encontramos los valores de f(m1 ) y
f(m2 ). Ahora, tenemos una de tres opciones:
3. f(m1 ) = f(m2 ) Podemos ver que ambos puntos pertenecen al área donde
se maximiza el valor de la función, o m1 está en el área de valores
crecientes y m2 está en el área de valores descendentes (aquı́ usamos
el rigor de la función aumentando / disminuyendo). Por lo tanto, el
espacio de búsqueda se reduce a [m1 ;m2 ]. Para simplificar el código,
este caso se puede combinar con cualquiera de los casos anteriores.
100
ARITMÉTICA Y ÁLGEBRA
suponer que después de eso la función alcanza su máximo en todos los pun-
tos del último intervalo [l; r]. Sin pérdida de generalidad, podemos tomar f(l)
como valor de retorno.
No existe una regla en la elección de los puntos m1 y m2 . Esta elección
definirá la tasa de convergencia y la precisión de la implementación. La forma
más común es elegir los puntos para que dividan el intervalo [l; r] en tres
partes iguales. Ası́, tenemos:
(r − l)
m1 = l +
3
(r − l)
m2 = r −
3
Si m1 y m2 se eligen para estar más cerca el uno del otro, la tasa de
convergencia aumentará ligeramente.
El análisis del tiempo de ejecucción viene dado por la siguiente expresión:
101
ARITMÉTICA Y ÁLGEBRA
En lugar del criterio r−l > eps, podemos seleccionar un número constante
de iteraciones como criterio de detención. El número de iteraciones debe
elegirse para garantizar la precisión requerida. Normalmente, en la mayorı́a
de los desafı́os de programación, el lı́mite de error es 10−6 y, por lo tanto,
son suficientes 200 a 300 iteraciones. Además, el número de iteraciones no
depende de los valores de l y r, por lo que el número de iteraciones establece
un error relativo requerido.
Ahora bien, si f(x) toma un parámetro entero, el intervalo [l; r] se vuelve
discreto. Como no impusimos ninguna restricción en la elección de los puntos
m1 y m2 , la exactitud del algoritmo no se ve afectada. m1 y m2 aún se pueden
elegir para dividir [l; r] en 3 partes aproximadamente iguales.
La diferencia se produce en el criterio de parada del algoritmo. La búsque-
da ternaria tendrá que detenerse cuando (r − l) < 3, porque en ese caso ya
no podemos seleccionar que m1 y m2 sean diferentes entre sı́, ası́ como de l
y r, y esto puede causar iteraciones infinitas. Una vez (r − l) < 3, el con-
junto restante de puntos candidatos (l, l + 1, ..., r) debe comprobarse para
encontrar el punto que produce el valor máximo f(x).
4.3.2. Newton-Raphson
Este es un método iterativo inventado por Isaac Newton alrededor de
1664. Sin embargo, a veces este método se denomina método de Raphson,
ya que Raphson inventó el mismo algoritmo unos años después de Newton,
pero su artı́culo se publicó mucho antes.
Su aplicación es la siguiente. Dada la siguiente ecuación:
f (x) = 0
102
ARITMÉTICA Y ÁLGEBRA
f (xi )
xi+1 = xi −
f 0 (xi )
Es intuitivamente claro que si la función f (x) es ”buena”(suave), y xi
está lo suficientemente cerca de la raı́z, entonces xi+1 estará aún más cerca
de la raı́z deseada.
La tasa de convergencia es cuadrática, lo que, condicionalmente hablando,
significa que el número de dı́gitos exactos en el valor aproximado xi se duplica
con cada iteración. Uno de los ejemplos donde se puede aplicar este método
es a la hora de calcular la raı́z cuadrada de un número, entremos en detalle.
√
Si sustituimos f (x) = x, Luego, después de simplificar la expresión,
obtenemos: Aplicación para calcular la raı́z cuadrada.
n
xi + xi
xi+1 =
2
La primera variante tı́pica del problema es cuando se da un número ra-
cional n, y su raı́z debe calcularse con cierta precisión eps:
103
ARITMÉTICA Y ÁLGEBRA
104
ARITMÉTICA Y ÁLGEBRA
Josefo, un historiador judı́o que vivió en el siglo I. Según lo que cuenta Josefo,
él y 40 soldados camaradas se encontraban atrapados en una cueva, rodeados
de romanos. Prefirieron suicidarse antes que ser capturados y decidieron que
echarı́an a suertes quién mataba a quién. Los últimos que quedaron fueron él
y otro hombre. Entonces convenció al otro hombre que debı́an entregarse a
los romanos en lugar de matarse entre ellos. Josefo atribuyó su supervivencia
a la suerte o a la Providencia.
El problema plantea lo siguiente:
Hay n personas paradas en un cı́rculo esperando a ser ejecutadas. Después
de que ejecutan a la primera persona, evitan a k-1 personas y la persona
número k es ejecutada. Entonces nuevamente, evitan a k-1 personas y la
persona número k es ejecutada. La eliminación continúa alrededor del cı́rculo
(que se hace cada vez más pequeño a medida que más personas son eliminadas
del mismo) hasta que sólo queda la última, que es liberada.
El objetivo es escoger el lugar inicial en el cı́rculo para sobrevivir (es el
último que queda), dados n y k.
Este problema se puede resolver modelando el procedimiento por fuerza
bruta pero su complejidad serı́a O(n2 ). Usando en un Range Tree quizas po-
damos implementar una solución con una complejidad de O(n log n). Veamos
si podemos encontrar alguna solución un poco más eficiente.
Primero vamos a intentar encontrar un patrón en la respuesta para el
problema Jn,k .
Usando fuerza bruta podemos modelar la situación como se muestra en
la siguiente tabla.
n\k 1 2 3 4 5 6 7 8 9 10
1 1 1 1 1 1 1 1 1 1 1
2 2 1 2 1 2 1 2 1 2 1
3 3 3 2 2 1 1 3 3 2 2
4 4 1 1 2 2 3 2 3 3 4
5 5 3 4 1 2 4 4 1 2 4
6 6 5 1 5 1 4 5 3 5 2
7 7 7 4 2 6 3 5 4 7 5
8 8 1 7 6 3 1 4 4 8 7
9 9 3 1 1 8 7 2 3 8 8
10 10 5 4 5 3 3 9 1 7 8
En esta tabla podemos ver no tan claramente (tomesé su tiempo, si quiere
no tenga pena saque papel y lápiz ) el siguiente patrón:
105
ARITMÉTICA Y ÁLGEBRA
J1,k = 1
Donde se toma como primera posición el valor 1 pero si tomará el la
posición 0 como la primera nos puede quedar más simple la primera expresión:
106
ARITMÉTICA Y ÁLGEBRA
Los tres métodos más utilizados para el cálculo del máximo común divisor
de dos números son:
5. Si m = pa1 ak b1 bk
1 ...pk y n = p1 ...pk , ai , bi
107
ARITMÉTICA Y ÁLGEBRA
int main ()
{
int gcd = GCD (3 ,6) ; // 3
return 0;
}
a · x + b · y = gcd(a, b)
Los cambios en el algoritmo original son muy simples. Todo lo que tene-
mos que hacer es descubrir cómo cambian los coeficientes x e y durante la
transición de (a, b) a (b mod a, a).
Supongamos que encontramos los coeficientes (x1 , y1 ) para (b mod a, a):
(b mod a) · x1 + a · y1 = g
108
ARITMÉTICA Y ÁLGEBRA
a·x+b·y =g
Podemos representar b mod a es:
b
b mod a = b − ·a
a
Al sustituir esta expresión en la ecuación del coeficiente de (x1 , y1 ) se
obtiene que:
b
g = (b mod a) · x1 + a · y1 = b − · a · x1 + a · y1
a
y después de reorganizar los términos:
b
g = b · x1 + a · y1 − · x1
a
Encontramos los valores x e y
(
x = y1 − ab · x1
y = x1
4.6.1. Implementación
109
ARITMÉTICA Y ÁLGEBRA
casos, las fórmulas anteriores se utilizan para volver a calcular los coeficientes
en cada iteración.
Esta implementación del algoritmo euclidiano extendido también produce
resultados correctos para enteros negativos.
ax + by = c
Donde a, b, c son enteros y x, y son enteros desconocidos.
En este artı́culo, consideramos varias situaciones que pueden ser proble-
mas clásicos en estas ecuaciones:
axg + byg = g
Si c es divisible por g = gcd(a, b), entonces la ecuación de diofantina dada
tiene una solución, de lo contrario no tiene ninguna solución. La prueba es
110
ARITMÉTICA Y ÁLGEBRA
x0 *= c / g ;
y0 *= c / g ;
if (a <0) x0 = - x0 ;
if (b <0) y0 = - y0 ;
return true ;
}
111
ARITMÉTICA Y ÁLGEBRA
void shiftSolution ( int &x , int &y , int a , int b , int cnt
) {
x += cnt * b ;
y -= cnt * a ;
}
112
ARITMÉTICA Y ÁLGEBRA
shiftSolution (x ,y ,a ,b ,( minx - x ) / b ) ;
if (x < minx )
shiftSolution (x ,y ,a ,b , sign_b ) ;
if (x > maxx )
return 0;
int lx1 = x ;
shiftSolution (x ,y ,a ,b ,( maxx - x ) / b ) ;
if (x > maxx )
shiftSolution (x ,y ,a ,b , - sign_b ) ;
int rx1 = x ;
shiftSolution (x ,y ,a ,b , -( miny - y ) / a ) ;
if (y < miny )
shiftSolution (x ,y ,a ,b , - sign_a ) ;
if (y > maxy )
return 0;
int lx2 = x ;
shiftSolution (x ,y ,a ,b , -( maxy - y ) / a ) ;
if (y > maxy )
shiftSolution (x ,y ,a ,b , sign_a ) ;
int rx2 = x ;
if ( lx > rx )
return 0;
return ( rx - lx ) / abs ( b ) +1;
}
Una vez que tenemos lx y rx , también es simple enumerar todas las solu-
ciones. Solo necesita iterar a través de x = lx +k · gb para todos los k ≥ 0 hasta
x = rx , y encontrar los valores de y correspondientes utilizando la ecuación
ax + by = c.
113
ARITMÉTICA Y ÁLGEBRA
b
x0 = x + k · ,
g
a
y0 = y − k · .
g
Tenga en cuenta que x + y cambian de la siguiente manera:
0 0 b a b−a
x +y =x+y+k· − =x+y+k·
g g g
a · x ≡ 1 mod m
114
ARITMÉTICA Y ÁLGEBRA
a·x+m·y =1
a · x ≡ 1 mod m
int x , y ;
int g = ex te nd ed _eu cl id ea n (a , m , x , y ) ;
if ( g !=1) {
cout << " No solution ! " ;
} else {
x = (x % m + m) % m;
cout << x << endl ;
}
aφ(m) ≡ 1 mód m
115
ARITMÉTICA Y ÁLGEBRA
am−1 ≡ 1 mód m
Multiplicamos ambos lados de las ecuaciones anteriores por a−1 , y obte-
nemos:
inv [1]=1;
for ( int i =2; i < m ;++ i )
inv [ i ]=( m -( m / i ) * inv [ m %i] %m ) %m ;
116
ARITMÉTICA Y ÁLGEBRA
Prueba
Tenemos:
jmk
m mód i = m − ·i
i
jmk
m mód i ≡ − · i mód m
i
jmk
−1 −1
(m mód i) · i · (m mód i) ≡− · i · i−1 · (m mód i)−1 mód m,
i
que se simplifica a:
jmk
−1
i ≡− · (m mód i)−1 mód m,
i
117
ARITMÉTICA Y ÁLGEBRA
118
ARITMÉTICA Y ÁLGEBRA
4.20. LU decomposition
4.21. Eigenvalue vector-specific
ARITMÉTICA Y ÁLGEBRA
(N ∗ (N ∗ N + 1))/2
3823 - The Birth of the Islanders Es claro que la solución del problema
radica en la construcción de un sucesión muy similar a la de los números
120
ARITMÉTICA Y ÁLGEBRA
de Fibonacci, casi identica dirı́a yo. Uno de los detalles del problema es la
cantidad de elementos pertenecientes que debemos calcular, los cuales son
números con una cantidad de dı́gitos significativa por lo que recomiendo
utilizar un lenguaje que soporte numeros grandes como Java o Python.
3763 - Playing with series Una vez leido el problema nos damos cuenta
que no estan dificil hallarle solución porque la cantidad de elementos que se
deben calcular de la serie es pequeña (solo los primeros 1000) ası́ que se puede
precalcular. La única dificultad radica en la rapidez con que puede crecer los
números que componen la serie pero eso soluble si se utiliza como lenguaje
de programación Java y si su clase BigInteger. Una vez hecho solo debemos
modular la respuesta.
121
ARITMÉTICA Y ÁLGEBRA
1661 - Brick Wall Patterns El problema nos pide hallar con cuantos
patrones de construcciónes se puede un muro de longitud n con dos unidades
de altura utilizando piezas cuyas dimensiones son 2x1. Cuando se analiza
detenidamente el problema nos podemos dar cuenta que para muros de lon-
gitud 1 y 0 la cantidad de patrones de construcciones que se pueden utilizar
es uno para construir muros con dichas longitudes. Lo otro que podemos
analizar que para un muro de longitud k la cantidad de patrones para cons-
truirlo va ser igual a la suma de la cantidad de patrones con los que se puede
construir un muro de longitud k-1 y un muro de longitud k-2. Dicho esto ya
estamos frente una sucesión bien famosa como son los números de Fibonacci.
La solución del problema es dado un n calcular el enesimo número Fibonacci,
siempre que n n sea distinto de cero en este caso debe detenerse el problema.
Se puede precalcular los Fibonacci y guardarlos en un arreglo.
1640 - Super 008 for Kids El problema nos determinar cuantos grupos de
exactamente K niños se puede formar con G niñas y B niños. Como restricción
que en el grupo debe tener al menos una niña y cuatro niños. Para solucionar
el problema debemos probar todas la variantes de conformación de un grupo
de K niños que cumplan con las restricciones de la cantidades mı́nimas de
niños y niñas en el grupo. Para estos vamos a:
Para b desde 4 hasta el min(B,K-1)
g=K-b
Si b>=4 y g>=1 y b<=B y g<=G y el la variante(b,g) no ha sido
analizada
Marco la variante (b,g) como analizada
solucion = solucion + CB,b *CG,g
Luego de forma similar:
Para g desde 1 hasta el min(G,K-4)
b=K-g
Si b>=4 y g>=1 y b<=B y g<=G y el la variante(b,g) no ha sido
analizada
Marco la variante (b,g) como analizada
solucion = solucion + CB,b *CG,g
Donde la variable solución su valor es cero inicialmente. Mientras CB,b y
CG,g son las combinaciones de escoger b niños de B disponibles y g niñas de
G disponibles. Se puede precalcular todas las posibles combinaciones que se
pueden utilizar con la utilización del triángulo de Pascal en una matriz de
dimensión 35 tanto en las filas como en columnas. Como el resultado puede
ser bastante grande recomiendo utilizar Java para trabajar con BigInteger.
Para mi solución utilice este lenguaje.
122
ARITMÉTICA Y ÁLGEBRA
1811 - Weird Numbers El problema nos pide realizar dada las posibles
entradas realizar tres posibles operaciones:
1933 - Og Una vez leido el ejercicio es fácil darse cuenta que solo debemos
hacer un programa que lea dos números para cada lı́nea hasta que ambos
números sean cero y por cada entrada de números solo debemos imprimir el
resultado de la suma de los dos numeros.
123
ARITMÉTICA Y ÁLGEBRA
4080 - Empty Water Tank El problema plantea que existe un tanque con
una capacidad C que necesita llenar en al menos T minutos con un sistema
de bombeo que es capaz de verter en el primer minuto una cantidad F, en el
segundo minuto F+1 , en el tercer minuto F+2 y ası́ sucesivamente en cada
minuto que pase el flujo de litro que se bombea aumenta en uno. Y se desea
hallar el F tal que el tanque se llene en al menos T minutos. Analizamos el
problema debemos dar solución a la siguiente expresión:
C <= F + (F + 1) + (F + 2) + (F + 1) + ... + (F + T − 1)
Simplificando la expresión nos queda:
T −1
X
C <= T F + i
i=1
T (T − 1)
C <= T F
2
Organizando la inecuación nos queda que:
T (T − 1)
TF >= C
2
Despejando F nos queda que :
T (T −1)
2
F >=
T
Una vez resuelta esta inecuación nos puede quedar un número decimal e
incluso negativo pero el problema nos dice que el flujo de bombeo inicial es
positivo y entero por lo que tenemos que reacomodar la respuesta según sea
el caso. Si nos da un flujo de bombeo inicial negativo entonces la respuesta
es 1. Si el flujo inicial sea positivo decimal debemos chequear si com el mayor
124
ARITMÉTICA Y ÁLGEBRA
S = (k + 2k + 3k + ... + wk) − n
= k(1 + 2 + 3 + ...w) − n
w(w + 1)
=k −n
2
Quedando la solución de la siguiente manera:
w(w + 1)
S = max(0, k − n)
2
Esto queda de esta manera porque puede darse el caso que la cantidad
de dolares inicial (n) sea suficiente para pagar las manzanas y no tenga que
pedir dinero.
125
ARITMÉTICA Y ÁLGEBRA
126
Capı́tulo 5
Ordenamiento
127
ORDENAMIENTO
1. Una lista pequeña necesitará menos pasos para ordenarse que una lista
grande.
128
ORDENAMIENTO
Aunque Heap sort tiene los mismos lı́mites de tiempo que merge sort,
requiere sólo O(1) espacio auxiliar en lugar del O(n) de merge sort, y es a
menudo más rápido en implementaciones prácticas. Quick sort, sin embargo,
es considerado por mucho como el más rápido algoritmo de ordenamiento de
propósito general. En el lado bueno, merge sort es un ordenamiento estable,
paraleliza mejor, y es más eficiente manejando medios secuenciales de acceso
lento. Merge sort es a menudo la mejor opción para ordenar una lista enlaza-
da: en esta situación es relativamente fácil implementar merge sort de manera
que sólo requiera O(1) espacio extra, y el mal rendimiento de las listas enla-
zadas ante el acceso aleatorio hace que otros algoritmos (como Quick sort)
den un bajo rendimiento, y para otros (como Heap sort) sea algo imposible.
int n ;
int niz [ MAX_N ] , tmp [ MAX_N ];
129
ORDENAMIENTO
{
tmp [ i ] = niz [ k ];
i ++;
}
}
else
{
for ( k = h ;k <= mid ; k ++)
{
tmp [ i ] = niz [ k ];
i ++;
}
}
for ( k = left ;k <= right ; k ++) niz [ k ] = tmp [ k ];
}
130
ORDENAMIENTO
int n ;
int niz [ MAX_N ];
int heap_size ;
131
ORDENAMIENTO
{
int pos = 1;
swap ( niz [ pos ] , niz [ heap_size - -]) ;
while ( pos <= heap_size )
{
int ret = pos ;
int left = pos *2;
int right = pos *2+1;
if ( left <= heap_size && niz [ left ] > niz [ ret ])
ret = left ;
if ( right <= heap_size && niz [ right ] > niz [ ret ])
ret = right ;
if ( ret != pos )
{
swap ( niz [ pos ] , niz [ ret ]) ;
pos = ret ;
}
else
break ;
}
}
int main ()
{
n = 5;
niz [1] = 4;
niz [2] = 2;
niz [3] = 5;
niz [4] = 1;
niz [5] = 3;
heap_size = n ;
132
ORDENAMIENTO
La lista queda separada en dos sublistas, una formada por los elementos
a la izquierda del pivote, y otra por los elementos a su derecha.
133
ORDENAMIENTO
int n ;
int niz [ MAX_N ];
134
ORDENAMIENTO
135
ORDENAMIENTO
ineficiente que existe, aunque para muchos programadores sea el más sencillo
de implementar.
int n ;
int niz [ MAX_N ];
inline void bubbleSort ()
{
bool swapped ;
int it = 0;
do
{
swapped = false ;
for ( int i =0; i <n - it -1; i ++)
{
if ( niz [ i ] > niz [ i +1])
{
swap ( niz [ i ] , niz [ i +1]) ;
swapped = true ;
}
}
it ++;
} while ( swapped ) ;
}
5.6. CountingSort
El ordenamiento por cuentas (counting sort en inglés) es un algoritmo de
ordenamiento en el que se cuenta el número de elementos de cada clase para
luego ordenarlos. Sólo puede ser utilizado por tanto para ordenar elementos
que sean contables (como los números enteros en un determinado intervalo,
pero no los números reales, por ejemplo).
El primer paso consiste en averiguar cuál es el intervalo dentro del que
están los datos a ordenar (valores mı́nimo y máximo). Después se crea un vec-
tor de números enteros con tantos elementos como valores haya en el intervalo
[mı́nimo, máximo], y a cada elemento se le da el valor 0 (0 apariciones). Tras
esto se recorren todos los elementos a ordenar y se cuenta el número de apa-
riciones de cada elemento (usando el vector que hemos creado). Por último,
basta con recorrer este vector para tener todos los elementos ordenados.
Se trata de un algoritmo estable cuya complejidad computacional es
O(n+k), siendo n el número de elementos a ordenar y k el tamaño del vector
auxiliar (máximo - mı́nimo).
136
ORDENAMIENTO
int n ;
int niz [ MAX_N ];
int Count [ MAX_K ];
void countingSort ()
{
for ( int i =0; i < n ; i ++)
Count [ niz [ i ]]++;
int ii = 0;
for ( int i =0; i < MAX_K ; i ++)
{
while ( Count [ i ] > 0)
{
niz [ ii ++] = i ;
Count [ i ] - -;
}
}
}
137
ORDENAMIENTO
int n ;
int niz [ MAX_N ];
inline void insertionSort ()
{
for ( int i =1; i < n ; i ++)
{
int j = i - 1;
int tmp = niz [ i ];
while ( j >= 0 && niz [ j ] > tmp )
{
niz [ j +1] = niz [ j ];
j - -;
}
niz [ j +1] = tmp ;
}
}
138
ORDENAMIENTO
Y en general
int n ;
int niz [ MAX_N ];
139
ORDENAMIENTO
140
ORDENAMIENTO
// Ejemplo #2
int array [] = { 23 , -1 , 9999 , 0 , 4 };
unsigned int array_size = 5;
141
ORDENAMIENTO
// Ejemplo #3
bool cmp ( int a , int b )
{
return a > b ;
}
...
142
ORDENAMIENTO
143
ORDENAMIENTO
144
ORDENAMIENTO
145
ORDENAMIENTO
5.10.1. C++
Con el lenguaje de programación existe dos variantes. La primera es muy
sencilla solo debemos implementar un método que reciba como parámetros
dos variables del tipo de dato que se desea ordenar. Dentro de la método se
compara las variables según el criterio definido para ese tipo de datos. Dicho
método devuelve el valor booleano. Luego solo debemos invocar algunos de
los métodos con sus dos parámetros tradicionales y como tercer parámetro
el nombre de la método de comparación creado. A continuación un ejemplo.
struct Submission
{
int time ;
char verdict [5];
};
......................
146
ORDENAMIENTO
int main ()
{
..............
..............
return 0;
}
struct Order
{
ULL begin , end , price ;
.......
147
ORDENAMIENTO
int main ()
{
.....................
.....................
return 0;
}
5.10.2. Java
En el caso del lenguaje de programación Java se puede utilizar la interface
Comparator permite ordenar listas y colecciones cuyos objetos pertenecen
a clases de tipo cualquiera. Esta interface permitirı́a por ejemplo ordenar
figuras geométricas planas por el área o el perı́metro. Su papel es similar al
de la interface Comparable, pero el usuario debe siempre proporcionar una
implementación de esta clase. Sus dos métodos se declaran en la forma:
148
ORDENAMIENTO
@Override
// 0 iguales , -1: a < b , 1: a > b
public int compareTo ( Equipo o )
{
if ( this . puntos > o . puntos )
return -1;
else if ( this . puntos < o . puntos )
return 1;
if ( this . victorias > o . victorias
)
return -1;
else if ( this . victorias < o .
149
ORDENAMIENTO
victorias )
return 1;
if ( this . diferenciaGoles () > o .
diferenciaGoles () )
return -1;
else if ( this . diferenciaGoles ()
< o . diferenciaGoles () )
return 1;
if ( this . golesFavor > o .
golesFavor )
return -1;
else if ( this . golesFavor < o .
golesFavor )
return 1;
if ( this . partidos < o . partidos )
return -1;
else if ( this . partidos > o .
partidos )
return 1;
return this . nombre .
c om p a re T o Ig n o re C a se ( o . nombre )
;
}
}
public static void main ( String [] args ) throws
IOException
{
....................
.......................
150
ORDENAMIENTO
3821 - Coco Olympiad Este ejercicio solo requiere que las notas de los
concursantes sean ordenadas de menor a mayor, eliminamos la menor y mayor
y se suma el resto esa suma se puede almacenar en una colección luego esa
colección se ordena de mayor a menor y se imprime el primer elemento.Otra
variante es tener una variable donde voy almacenar la suma de las notas que
sea mayor, para esto se inicializa dicha variable con un valor bien pequeño
y luego de sumar las notas del conursante si esta es mayor que el valor
almacenado en la variable almacenamos dicha variable ese nuevo valor. Luego
se imprime la variable.
1089 - Open Source Para darle solución al problema vamos a crear pri-
meramente una estructura llamada Proyecto que va tener como campos, una
cadena para almacenar el nombre del proyecto y un entero para almacenar
la cantidad de integrantes de ese proyecto. Vamos utilizar dos estructura de
datos extras. La primera va ser un cojunto de cadenas donde se va alma-
cenar todos los usuarios registrados , mientras utilizaremos un diccionario
donde las claves serán los nombres de los usuarios y el valor asociado será
la posición que ocupa el proyecto en el arreglo de proyectos al que quieren
asociarse. Cada vez que se lea un usuario debemos chequear lo siguiente:
Si usuario no pertence al conjunto de usuarios:
Insertamos el usuario al cojunto de usuarios.
151
ORDENAMIENTO
152
ORDENAMIENTO
Si eliminarSSJimmy es verdadero
La fuerza del bando de Jimmy debe decrementar en ssJimmy
Si eliminarSSManny es verdadero
La fuerza del bando de Manny debe decrementar en ssManny
Luego solo tenemos que chequear que bando se quedo sin soldados y cual
tiene y ese será el ganador, recuerde que si ambos tienen soldados significa
que terminó empatado el juego.
1318 - Abc El ejercicio nos pide dado tres números y una secuencia de
las letras ’A’, ’B’ y ’C’. Imprimir los números de acuerdo a la secuencia de
letras donde ’A’ es el menor valor de los tres, ’B’ es la segundo menor valor,
mientras ’C’ es el mayor de los tres números.
3986 - Playing with digits Una vez leı́do el problema nos podemos per-
catar que debemos leer la cadena entrante que solo va estar compuesta por
digitos exceptuando el digito 0. De esta entrada debemos imprimir los di-
gitos presentes ordenados de forma descendentemente según la cantidad de
ocurrencias de los mismos en la entrada. Segun el problema si dos digitos
tienen la misma frecuencia decide el valor númerico de los digitos. Para so-
lucionar el problema podemos crear una estructura con dos atributos (valor
y frecuencia). Inicializo un arreglo de 10 elementos de esta estructura donde
cada posición i, la estructura en esa posición su valor será igual i y frecuen-
cia igual 0. Leo la secuencia y la recorro y por cada dı́gito de la secuencia
voy a su posición en el arreglo e incremento en uno la frecuencia. Ordeno
los elementos de las estructura (primero por la frecuencia y por el valor) .
Imprimo los valores de los elementos después de ordenarlo descendentemente
y siempre que la frecuencia de dicho valor sea distinto de 0.
4207 - Pedro Picapiedras Una vez leı́do el problema podemos ver que
nos piden a partir de un grupo de ruedas de radios variables tenemos que
calcular cuantos carros se pueden armar de dichas ruedas y cuantas ruedas
sobran. Se conoce que un carro necesita la cuatros ruedas con similar radio.
Un dato interesante del problema es que el rango de radio de las ruedas es
menor que la cantidad de posibles ruedas a analizar por lo que para resolver
el problema bien pudieramos aplicar una estrategia parecida al método de
ordenamiento CountingSort. Vamos a crear un arreglo y el la posición i del
arreglo vamos alamacenar la cantidad de ruedas cuyo radio es igual a i de
esta forma para dar solución al problema solo debemos recorrer cada posición
de arreglo tomamos el valor de la posición y lo dividimos entre cuatro y esa
va ser la cantidad carros con ruedas de ese tipo que se pueden armar y si
153
ORDENAMIENTO
MOG13B - Phone List La misma idea del problema 2352 - Phone List.
DMOJ - POI El ejercicio nos pide hallar la puntación y el lugar que alcan-
za un determinado concursante durante un concurso. Para esto nos explica
como se calcula la puntación de cada tarea que integra el concurso, las tareas
hecha por cada concursante. Para resolver el problema solo debemos tener
una estructura de tipo concursante que almacene su identificador asi como
los identificadores de las tareas que realizo y los puntos que este obtuvo. El
primer paso es calcular para cada tarea cuanto puntos aporta su realización.
Luego por cada concursantes vemos las tareas que realizo y podemos sacar
su puntaje final. Una vez realizado esto podemos ordenar los concursantes
154
ORDENAMIENTO
155
ORDENAMIENTO
156
Capı́tulo 6
Búsqueda
157
BÚSQUEDA
{
bool res ;
if ( principio <= fin )
{
int m = (( principio + fin ) /2) + principio ;
if ( x < v [ m ])
res = b u sq u e da _ d ic o t om i c a (v , principio , m
-1 , x ) ;
else if ( x > v [ m ])
res = b u sq u e da _ d ic o t om i c a (v , m +1 , fin , x ) ;
else
res = true ;
}
else
res = false ;
return res ;
}
int n ;
int niz [ MAX_N ];
158
BÚSQUEDA
159
BÚSQUEDA
{
if ( binary_search ( nums + start , nums + end , i
) )
{
cout << " nums [] contains " << i << endl ;
}
else
{
cout << " nums [] DOES NOT contain " << i
<< endl ;
}
}
160
BÚSQUEDA
161
BÚSQUEDA
int n , k ;
int niz [ MAX_N ];
162
BÚSQUEDA
}
swap ( niz [ pivotIndex ] , niz [ right ]) ;
if ( pivotIndex == k ) return niz [ pivotIndex ];
else if ( k < pivotIndex ) return kselect ( left ,
pivotIndex -1 , k ) ;
else return kselect ( pivotIndex +1 , right , k ) ;
}
else return niz [ left ];
}
int main ()
{
n = 5 , k = 3;
niz [0] = 4;
niz [1] = 2;
niz [2] = 5;
niz [3] = 1;
niz [4] = 3;
printf ( " %d \ n " , kselect (0 , n -1 , k ) ) ;
return 0;
}
163
BÚSQUEDA
1005 - Rent your Airplane and make Money La solución del pro-
blema parte de ordenar las órdenes de acuerdo al tiempo de culminación de
cada uno. Una vez ordenado debemos buscar para cada orden k cual orden i
donde la diferencia entre el tiempo culminación de la orden k y el tiempo de
comienzo de la orden i sea menor siempre con la restricción que el tiempo de
comienzo de la orden i sea mayor que el tiempo de culminación de la orden k,
esta búsqueda la podemos hacerde forma binaria para hacerla más eficiente.
Si existe una orden i entonces el precio de la orden i será igual al precio
de la orden k mas el precio de la orden i. Una vez hecho esto solo debemos
chequear entre la orden k y el orden k-1 cual precio es el mayor y actualizar
con ese valor el precio de la orden k. La solución del problema va estar en la
última orden de la lista ordenada.
164
BÚSQUEDA
165
BÚSQUEDA
166
BÚSQUEDA
4121 - Scheduling Una vez leı́do el problema podemos ver que su mode-
lación y solución en muy identica a las soluciones de los ejercicios 3529 - How
to See More Games, 1005 - Rent your Airplane and make Money y 1593 -
Soccer in 2014 los cuales feuron explicados anteriormente en este apartado.
DMJO Números Primos de nuevo Una vez leido el problema nos pode-
mos percatar que le problema radica en dado un valor X debemos encontrar
el mayor número primo que sea menor que X y el menor número primo
mayor que X. Para solucionar el problema vamos a partir de calcular con
una criba todos los primos menores de 106 y almacenarlos en una colección
ordenados de forma ascedente. Una vez hecho esto si utilizamos C++ solo
debemos chequear si X es un número primo esto se puede hacer con una
búsqueda binaria sobre la colección de numeros primos precalculada. Si esto
ocurre entonces los dos números buscados es el propio X. En caso de X no
sea un número primo con las función lower bound como parámetros la colec-
ción de números primos y X encuentra el menor número primo mayor que X
mientras el mayor número primo menor que text va ser el primo anterior en
la colección del hallado con la función lower bound.
167
BÚSQUEDA
168
Capı́tulo 7
Cadena
169
CADENA
return v ;
}
170
CADENA
171
CADENA
172
CADENA
if (++ k >= m )
{
++ count ; // match at t [i - m + 1 .. i ]
k = fail [ k ];
}
}
return count ;
}
P[i] = j;
}
173
CADENA
j = P [ j ];
}
}
return matches ;
}
174
CADENA
175
CADENA
Tabla primera
Rellénese la primera tabla como sigue. Para cada i menor que la longitud
de la cadena de búsqueda, constrúyase el patrón consistente en los últimos i
caracteres de la cadena precedida por un carácter no-coincidente, alinéense
a la derecha el patrón y la cadena, y anótese el menor número de caracteres
para que el patrón tenga que desplazarse a la izquierda para una coincidencia.
Por ejemplo, para la búsqueda de la cadena ANPANMAN, la tabla serı́a
como sigue: (NMAN significa una subcadena en ANPANMAN consistente en
un carácter que no es ’N’ más los caracteres ’MAN’.)
Tabla segunda
La segunda tabla es fácil de calcular: iniciése en el último carácter de
la cadena vista y muévase hacia el primer carácter. Cada vez que usted se
mueve a la izquierda, si el carácter sobre el que está no está ya en la tabla,
añádalo; su valor de desplazamiento es la distancia desde el carácter más a
la derecha. Todos los otros caracteres reciben un valor igual a la longitud de
la cadena de búsqueda.
Para la cadena ANPANMAN, la segunda tabla serı́a como se muestra
(por claridad, las entradas son mostradas en el orden que serı́an añadidas a
176
CADENA
Carácter Desplazamiento
A 1
M 2
N 3
P 5
caracteres restantes 8
177
CADENA
178
CADENA
}
return count ;
}
7.3. Palı́ndromo
Un palı́ndromo (del griego palin dromein, volver a ir hacia atrás) es una
palabra, número o frase que se lee igual hacia adelante que hacia atrás. Si se
trata de un número, se llama capicúa. Habitualmente, las frases palindrómi-
cas se resienten en su significado cuanto más largas son. Muchos son los
problemas de cadenas que involucra este concepto.
179
CADENA
a]s]d]b]a]a]b]a]s]d
1010101014103010101
180
CADENA
181
CADENA
cadenas exponenciales. Si solo queremos que esta función hash distinga en-
tre todas las cadenas que consisten en caracteres en minúscula de longitud
menor a 15, entonces el hash ya no cabrı́a en un entero de 64 bits (por ejem-
plo, sin signo largo largo), porque hay muchos de ellos. Y, por supuesto, no
queremos comparar enteros largos arbitrarios, porque esto también tendrá la
complejidad O(n).
Por lo general, queremos que la función hash asigne cadenas a números
de un rango fijo [0, m), luego, comparar cadenas es solo una comparación
de dos enteros con una longitud fija. Y, por supuesto, queremos que hash
hash(s) 6= hash(t) sea muy probable, si s 6= t.
Esa es la parte importante que tienes que tener en cuenta. El uso de hash
no será 100 % determinı́sticamente correcto, porque dos cadenas diferentes
pueden tener el mismo hash (los hashes chocan). Sin embargo, en una am-
plia mayorı́a de problemas donde son utilizados, esto puede ser ignorado de
manera segura ya que la probabilidad de que dos hashes diferentes colisio-
nen es muy pequeña. Mas adelante discutiremos algunas técnicas sobre cómo
mantener muy baja la probabilidad de colisiones.
n−1
X
hash(s) = s[0]+s[1]·p+s[2]·p2 +...+s[n−1]·pn−1 mod m = s[i]·pi mod m
i=0
182
CADENA
2. Cálculo del número de diferentes subcadenas de una cadena en O(n2 log n).
7.4.2. Colisión
Una colisión ocurre cuando dos valores de entrada diferente generan el
mismo resumen. Una función hash debe ser resistente a la colisión.
El mismo hash siempre será el resultado de los mismos datos (funciones
deterministas), pero la modificación de la información, aunque sea un solo
bit dará como resultado un hash totalmente distinto.
183
CADENA
184
CADENA
185
CADENA
186
CADENA
int cnt = 0;
for ( int l = 1; l <= n ; l ++) {
set < long long > hs ;
for ( int i = 0; i <= n - l ; i ++) {
long long cur_h = ( h [ i + l ] + m - h [ i ]) % m ;
cur_h = ( cur_h * p_pow [n -i -1]) % m ;
hs . insert ( cur_h ) ;
}
cnt += hs . size () ;
}
return cnt ;
}
187
CADENA
dentro del texto que coinciden con el patrón recorro cada posición del vector y
pidiendo de la cadena la subcadena (vector[i], vector[i]+longitud del patrón)
compruebo que esa subcadena sea igual a la palabra que disgusta si ocurre
esto cuento un remplazo y en la última posición de esta subcadena sustituyo
el carácter por el de interrogación.
188
CADENA
aclara que una palabra palindrome es aquella que se lee lo mismo de derecha
a izquierda o viceversa. Mientras una palabra bipalindrome es aquella que no
es palindrome pero puede ser dividida en exactamente en dos bipalindrome,
mientras una palabra no palindrome es aquella que no es ninguna de las dos
anteriores. Dicho esto no es dificil ver que podemos hacer dos metodos uno
para que chequee si la palabra es palindrome y otro para que cheque si es
bipalindrome. En caso del caso basta con copiar el valor para otra variable
aplicarle el método reverse1 a la copia y luego compararla con la original.
En caso del método que me determina si la palabra es bipalindrome vamos
hacer el siguiente análisis.
Una palabra que empieza con un cáracter x y termina con el cáracter z
para que sea bipalindrome debe contar con la secuencia xz en su interior para
poder dividir la palabra por esa secuencia de forma que tal que me queda
una palabra que comienza y temina con el cáracter x y otra que comienza y
termina con z. Esto nos indica que para saber si una palabra es bipalindrome
solo debemos buscar las ocurrencias de la secuencias xz (donde x y z son los
caracteres por los cuales comienza y termina la palabra respectivamente) en
la palabra y por cada ocurrecia ver si la division por esa secuencia genera
dos palabras palindromes. Con una ocurrencia que cumpla ya la palabra es
bipalindrome. Para buscar la secuencia xz dentro de la palabra podemos usar
un KMP.
Una vez que la palabra no sea palindrome ni bipalindrome entonces será
no palindrome
En una tercera lı́nea las letras ordenadas alfabeticamente que son co-
munes entre las dos palabras. En otras palabras la intersección entre el
1
La función reverse pertenece a la biblioteca algorithms del lenguaje C++
189
CADENA
4120 - Perfect strings Una vez que se lee el problema nos podemos per-
catar que no posee una alta complejidad. Para que una cadena pueda ser
considerada perfect solo debemos chequear que la cantidad de vocales de la
palabra sea superior a la cantidad de consonantes de la palabra mas uno.
Siempre que se cumpla n > m + 1 va existir un reordenamiento tal que cada
consonante de la palabra va estar entre dos vocales.
190
CADENA
191
CADENA
192
Capı́tulo 8
Combinatoria
8.1. Variaciones
Se llama variación de los n objetos tomados p a p, a todo conjunto orde-
nado formado por p objetos escogidos de cualquier modo entre los n objetos
considerando distintas dos variaciones cuando difieran en algún objeto o en
el orden.
Ejemplo: Con los cuatro objetos a, b, c, d las variaciones dos a dos son:
ab ba ca da
ac bc cb db
ad bd cd dc
El número de estas variaciones lo denotaremos por Vn,p
Las variaciones con cierto número de objetos dados a, b, c, d, e se pueden
ir formando sucesivamente (primero las monarias, luego las binarias, después
las ternarias, etc.) por un método uniforme que consiste en agregar a cada
variación de cierto orden cada una de las letras (objetos) que no están en
ellas. Las variaciones monarias (de primer orden) o variaciones tomadas uno
a uno con las cinco letras a, b, c, d, e son evidentemente:
abcde
Para formar las binarias agregamos a cada una las letras restantes y se
obtiene el cuadro:
ab ba ca da ea
ac bc cb db eb
ad bd cd cd ec
193
COMBINATORIA
ae be ce de ed
Se forman ahora las ternarias agregando sucesivamente a cada binaria
las letras que no están en ella, como hay 20 binarias y a cada una se le
puede agregar 5 - 2 = 3 letras, resultarán 60 variaciones ternarias. Siguiendo
el mismo proceso se forman las variaciones de cuarto orden y las de quinto
orden.
Sea Vn,p el número de variaciones de orden p y Vn,p−1 el número de varia-
ciones de orden p-1.
Una vez formado el cuadro de variaciones de orden p-1, para formar el de
orden p se le agrega a cada una los n - (p - 1) = n – p + 1 elementos que no
están en ella: por lo tanto cada variación de orden p - 1 produce n – p + 1
variaciones de orden p y como hay Vn,p−1 variaciones de orden p-1 el número
total de variaciones de orden p será:
Vn,p−1 = n ∗ (n − 1) ∗ (n − 2) ∗ ... ∗ (n–p + 1)
8.2. Permutaciones
Se llama permutación de los n objetos a todo conjunto ordenado for-
mado por dichos n elementos. Dos permutaciones se distinguen una de otra
solamente por el orden de colocación de sus elementos.
Las permutaciones son un caso particular de las variaciones, cuando p =
n. Es decir, son las variaciones de n objetos tomados n a n.
Denotaremos por Pn al número de permutaciones de n objetos.
Pn = Vn,n
194
COMBINATORIA
8.3. Combinaciones
Se llama combinación de n objetos tomados p a p, a todo conjunto de
p objetos elegidos entre ellos de tal modo que dos conjuntos se diferencien al
menos en un objeto.
Denotaremos por Cn,p al número de combinaciones de n objetos tomados
p a p.
Las combinaciones de n objetos tomados p a p son los distintos conjuntos
que pueden formarse con p objetos elegidos entre n dados, de modo que un
conjunto se diferencie de otro al menos en uno de los elementos.
195
COMBINATORIA
196
COMBINATORIA
1
En matemática, el triángulo de Pascal es una representación de los coeficientes bi-
nomiales ordenados en forma triangular. Es llamado ası́ en honor al matemático francés
Blaise Pascal, quien introdujo esta notación en 1654, en su Traité du triangle arithmétique.
197
COMBINATORIA
198
COMBINATORIA
{
for ( int i =1; i <= lasti ; i ++)
{
for ( int j = lastj +1; j <= k ; j ++)
{
binom [ i ][ j ] = binom [i -1][ j -1] +
binom [i -1][ j ];
}
}
lastj = k ;
}
if ( lasti >= n )
return binom [ n ][ k ];
else
{
for ( int i = lasti +1; i <= n ; i ++)
{
binom [ i ][0] = 1;
for ( int j =1; j <= lastj ; j ++)
{
binom [ i ][ j ] = binom [i -1][ j -1] +
binom [i -1][ j ];
}
}
lasti = n ;
return binom [ n ][ k ];
}
}
}
199
COMBINATORIA
b /= g ;
}
200
COMBINATORIA
int y = 2 ;
201
COMBINATORIA
int x = 1 0 , y = 2 0 ; // x en b i n a r i o e s 0 1 0 1 0 , y en b i n a r i o
10100
int z = x & y ; // e q u i v a l e a : i n t z = x b i t a n d y ; z
toma e l v a l o r 00000 e s d e c i r 0
Conjunto potencia
Se denomina conjunto potencia del conjunto S a al conjunto de todos los
subconjuntos de S, ejemplo:
Sea S= {a, b, c} un conjunto conformado por los elementos a, b y c el
conjunto potencia de S es {{a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c},
{Ø}}
Por anterior expuesto se puede decir que la cantidad de subconjuntos de
un conjunto va ser 2 a la potencia de la cardinalidad del conjunto teniendo
como cardinalidad de un conjunto la cantidad de elementos de este. En este
caso el conjunto S tiene cardinalidad 3 y la cantidad de subconjuntos es 8.
Máscara de bit
La técnica de la máscara de bit se encarga de combinar los dos temas
abordados en esta presentación anteriormente para eso tomemos como caso
de estudio tomemos el conjunto S= {a, b, c} del cual conformaremos todos
los posibles subconjunto aplicando la técnica máscara de bit.
Bien como sabemos el conjunto S tiene 8 subconjuntos, pues bien vamos a
construir una pequeña tabla con tres columnas en la primera columna estarán
todos los números desde el 0 hasta el 7, en la segunda la representación binaria
del número a la izquierda mientras en la tercera estarán los elementos del
202
COMBINATORIA
203
COMBINATORIA
de elementos del conjunto inicial. Por lo que dicha técnica es aplicable para
n menor igual que 20 ya 21 habrı́a que analizar el tiempo lı́mite del problema
ası́ como las adecuaciones hechas dentro de este algoritmo incluso con 20
elementos hay que hilar una óptima solución.
Para n igual 20 el número de iteraciones es 20971520 mientras para 21 es
44040192.
8.5. Backtracking
Dentro de las técnicas de diseño de algoritmos, el método de Vuelta Atrás
(del inglés Backtracking) es uno de los de más ámplia utilización, en el senti-
do de que puede aplicarse en la resolución de un gran número de problemas,
muy especialmente en aquellos de optimización.Permite hacer búsqueda sis-
temática a través de todas las configuraciones posibles dentro de un espacio
de búsqueda.
Para lograr esto, los algoritmos de tipo backtracking construyen posibles
soluciones candidatas de manera sistemática. En general, dado una solución
candidata s:
204
COMBINATORIA
- Un problema clásico que puede ser resuelto con un diseño Vuelta Atrás
es el denominado de las ocho reinas y en general, de las n reinas. Dispo-
nemos de un tablero de ajedrez de tamaño 8 x 8, y se trata de colocar
en él ocho reinas de manera que no se amenacen según las normas del
ajedrez, es decir, que no se encuentren dos reinas ni en la misma fila,
ni en la misma columna, ni en la misma diagonal.
205
COMBINATORIA
del tablero sean visitadas una sola vez, si tal secuencia de movimientos
existe.
- Supongamos que tenemos n hombres y n mujeres y dos matrices M y
H que contienen las preferencias de los unos por los otros. Más con-
cretamente, la fila M[i,·] es una ordenación (de mayor a menor) de las
mujeres según las preferencias del i-ésimo hombre y, análogamente, la
fila H[i,·] es una ordenación (de mayor a menor) de los hombres según
las preferencias de la i-ésima mujer. El problema consiste en diseñar
un algoritmo que encuentre, si es que existe, un emparejamiento de
hombres y mujeres tal que todas las parejas formadas sean estables.
Diremos que una pareja (h,m) es estable si no se da ninguna de estas
dos circunstancias:
1. Existe una mujer m’ (que forma la pareja (h’,m’)) tal que el hom-
bre h la prefiere sobre la mujer m y además la mujer m’ también
prefiere a h sobre h’.
2. Existe un hombre h” (que forma la pareja (h”,m”)) tal que la
mujer m lo prefiere sobre el hombre h y además el hombre h”
también prefiere a m sobre la mujer m”.
- Una matriz bidimensional n x n puede representar un laberinto cua-
drado. Cada posición contiene un entero no negativo que indica si la
casilla es transitable (0) o no lo es (∞). Las casillas [1,1] y [n,n] corres-
ponden a la entrada y salida del laberinto y siempre serán transitables.
Dada una matriz con un laberinto, el problema consiste en diseñar un
algoritmo que encuentre un camino, si existe, para ir de la entrada a la
salida.
- Dadas n personas y n tareas, queremos asignar a cada persona una
tarea. El coste de asignar a la persona i la tarea j viene determinado
por la posición [i,j] de una matriz dada (TARIFAS). Diseñar un algo-
ritmo que asigne una tarea a cada persona minimizando el coste de la
asignación.
- Sea W un conjunto de enteros no negativos y M un número entero
positivo. El problema consiste en diseñar un algoritmo para encontrar
todos los posibles subconjuntos de W cuya suma sea exactamente M.
- Dado un grafo conexo, se llama Ciclo Hamiltoniano a aquel ciclo que
visita exactamente una vez cada vértice del grafo y vuelve al punto de
partida. El problema consiste en detectar la presencia de ciclos Hamil-
tonianos en un grafo dado.
206
COMBINATORIA
Y también satisfacen, que puede ser una forma más eficiente de calcular-
los.
Existen múltiples problemas de concurso de programación cuya solución
la dan los números de Catalan o son parte ella. El libro Enumerative Combi-
natorics: Volume 2, de Richard P. Stanley contiene un conjunto de ejercicios
que describen 66 interpretaciones distintas de los números de Catalan. A
continuación vamos algunos ejemplos.
Cn es el número de palabras de Dyck de longitud 2n. Una palabra de
Dyck es una cadena de caracteres que consiste en n X’s y n Y’s de
forma que no haya ningún segmento inicial que tenga más Y’s que X’s.
207
COMBINATORIA
8.7. K-Combinations
8.8. Permutations
8.9. Power Set
8.10. Principio de inclusión exclusión
8.11. Análisis de ejercicios
2739 - Coco-Bits and the Guidance Race Generando todos los posi-
bles recorridos desde el punto inicial hasta el final pasando por todos puntos
208
COMBINATORIA
2697 - Magic Star Dado que son solo 12 letras se puede implementar
backtracking que genere una posible combinación siempre respetando las
posibles letras fijas , que la combinación sea alfabeticamente menor y la
suma siempre debe ser 26.
1900 - John and his Sheeps Dada las condiciones del problema se puede
hacer una DFS recursivo que genere todas los posibles recorridos desde la
celda (1,1) hasta la celda (N,N). Claro esta variante lleva adeuaciones porque
si generamos todas las combinaciones hasta el final nos puede conducir un
TLE. Ahora si al DFS recursivo le aplicamos la técnica de poda y corte
podemos llevar un TLE a AC. Para hacer el corte o poda en el DFS recursivo
vamos a visitar el próximo nodo siempre y cuando no halla derteminado la
longitud de un posible camino. En caso de que ya tenga una longitud previa
solo voy a visitar el próximo nodo solo si la distancia recorrida hasta ese
momento mas la distancia de ir del nodo actual al deseado es menor o igual
la distancia calculada previamente. En caso de que todos los nodos estén
visitados sumar la distancia recorrida mas la distancia desde el último nodo
visitado hasta la celda (N,N) y si dicha distancia es menor que la que se tiene
como referencia actualizar la referencia con ese valor.
209
COMBINATORIA
3640 - Triples of Even Parity El problema nos pide que dada una co-
lección de números al cual se les puede eliminar e insertar números con la
restricciones que cuando se inserta un elemento si este ya existe en la colec-
ción no se procede a insertar y de igual forma cuando se elimina un elemento
no almacenado no se elimina, calcular en cualquier momento la cantidad tria-
da que puedo formar con la condición que la suma de los elementos sea par.
Para que una triada de números su suma sea par existen dos forma (P,P,P) y
(I,I,P) donde I representa un número impar mientras P representa un núme-
ro par. Este pequeño análisis nos conduce a la ecuación que nos resuelve el
problema la cual es CN,3 + CM,2 ∗ N . Donde N es la cantidad de números
pares y M la cantidad de números impares almacenados hasta el momento
de la pregunta. CN,3 es la cantidad de forma de seleccionar 3 elementos de
N sin de tal modo que dos conjuntos se diferencien al menos en un objeto.
De igual forma sucede para la expresión CM,2 . Para controlar la inserción y
eliminación de los números, se puede utilizar una estructura de tipo conjunto
una para los números pares y otra para números impares.
1958 - Grazing Patterns Dada una matriz de 5x5 tenemos vacas situadas
en (1;1) y (5;5) las cuales se mueven paralelo a los ejes hacia una celda que
tenga pasto. Determinar la cantidad de forma que las vacas terminan en la
misma celda en la matriz despues de consumir todas las celdas que tenga
pasto. Existen celdas en la matriz que no tienen pasto inicialmente .
Realizar un DFS de manera recursiva donde en cada llamada las dos
vacas avacen una celda que tenga pasto. Tener en cuenta que las las dos
vacas pueden acceder a la misma casilla en un mismo turno. El DFS debe
recibir como parametros las posiciones de las vacas y la cantidad de celdas en
la matriz que no tienen pasto hasta ese momento. Cuando las posiciones son
las misma y la cantidad de celdas sin pastos es 25 entonces hemos encontrado
una forma. Si las posiciones son distintas entonces buscar dos nuevas celdas
para donde se pueda mover las vacas y volver a invocar el metodo con estas
nuevas posiciones. Las posiciones de las vacas puede ser una estructura que
indique fila y columna.
210
COMBINATORIA
211
COMBINATORIA
3463 - Onerous Oracle Dado un valor N se quiere generar todas las cade-
nas posibles que las primeras letras sean C las intermedias A y las últimas T
y de forma tal que la palabra CAT este como subcadena no necesariamente
concecutiva dentro de la palabra una cantidad de veces igual a N.
La idea es buscar todas las trı́adas posibles sin repetir (a,b,c) tal que
a∗b∗c = N y cada vez que se encuentren una trı́ada que cumpla la condición,
la palabra para ese caso va a tener una cantidad de a de C, una cantidad b
de A y una cantidad c de T.
Cada palabra conformada se almacena en un set para evitar duplicados
y luego debe ser ordenada esta colección de forma similar a como se pide el
formato de salida del problema para facilitar el proceso de impresión.
212
COMBINATORIA
213
COMBINATORIA
214
Capı́tulo 9
Teorı́a de número
215
TEORÍA DE NÚMERO
216
TEORÍA DE NÚMERO
// { 2 , 7 , 6 1 , − 1} para n <4759123141 (= 2 ˆ 3 2 )
// { 2 , 3 , 5 , 7 , 1 1 , 1 3 , 1 7 , 1 9 , 2 3 , − 1} para n <10 ˆ 16 ( h a s t a
e l momento )
bool i s P r i m e ( int n )
{
i f ( n<=1 | | ( n>2 && n %2==0))
return f a l s e ;
int t e s t [ ] = { 2 , 3 , 5 , 7 , 1 1 , 1 3 , 1 7 , 1 9 , 2 3 , − 1 } ;
int d = n − 1 , s = 0 ;
while ( d %2==0) ++s , d/= 2 ;
v e c t o r <int> p r i m e s ;
bool mark [MAX N ] ;
217
TEORÍA DE NÚMERO
i n l i n e void s i e v e ( int B)
{
i f (B > 1 ) p r i m e s . push back ( 2 ) ;
f o r ( int i =3; i<=B ; i +=2)
{
i f ( ! mark [ i ] )
{
mark [ i ]= true ;
p r i m e s . push back ( i ) ;
i f ( i<=s q r t (B) +1)
f o r ( int j=i ∗ i ; j<=B ; j+=i )
mark [ j ]= true ;
}
}
}
La complejidad del anterior algoritmo es O(N log log N ) lo que hace que
sea un algoritmo bastante rapido. El problema del algoritmo radica en el
costo de memoria ya que necesita un arreglo de N+1 elementos siendo N el
máximo número hasta donde deseamos conocer todos los números primos.
Por lo que el algoritmo solo es aconsejable usarlo cuando N no es mayor de
106 , para un intervalo mayor se puede este algoritmo junto con otras tecnicas
como los test de Primalidad.
Otra variante de dicho algoritmo el cual posee similar complejidad pero
devuelve un vector donde si primes[i]!=0 entonces i es un número primo es
la siguiente
v e c t o r <int> s i e v e o f e r a t o s t h e n e s ( int n )
{
v e c t o r <int> p r i m e s ( n ) ;
f o r ( int i = 2 ; i <n ; ++i )
primes [ i ] = i ;
f o r ( int i = 2 ; i ∗ i <n ; ++ i )
i f ( primes [ i ] )
f o r ( int j = i ∗ i ; j <n ; j+=i )
primes [ j ] = 0 ;
return p r i m e s ;
}
218
TEORÍA DE NÚMERO
Todos los restos son módulo 60, es decir, se divide el número entre 60
y se toma el resto.
219
TEORÍA DE NÚMERO
void s i e v e o f a t k i n ( )
{
int n ;
f o r ( int z = 1 ; z <= 5 ; z+=4)
{
f o r ( int y = z ; y<= s q r t (N) ; y+=6)
{
f o r ( int x = 1 ; x <= s q r t (N) && ( n=4∗x∗x+y∗y )<= N; ++x )
isprime [ n]=! isprime [ n ] ;
f o r ( int x = y + 1 ; x <= s q r t (N) && ( n=3∗x∗x−y∗y )<=N; x
+=2)
isprime [ n]=! isprime [ n ] ;
}
}
220
TEORÍA DE NÚMERO
{
f o r ( int y = z ; y<=s q r t (N) ; y +=6)
{
f o r ( int x = 1 ; x<=s q r t (N) && ( n=3∗x∗x+y∗y )<=N; x+=2)
isprime [ n]=! isprime [ n ] ;
f o r ( int x = y+1;x<=s q r t (N) && ( n=3∗x∗x−y∗y )<=N; x+=2)
isprime [ n]=! isprime [ n ] ;
}
}
void s i e v e o f a t k i n ( )
{
int n ;
f o r ( int x = 1 ; x<=s q r t (N) ; ++x )
{
f o r ( int y = 1 ; y<=s q r t (N) ; ++y )
{
n= 4∗ x∗x+y∗y ;
i f ( n <=N && ( n %12==1 | | n %12==5))
isprime [ n]=! isprime [ n ] ;
n=3∗x∗x+y∗y ;
i f ( n<=N && n %12==7)
isprime [ n]=! isprime [ n ] ;
n=3∗x∗x−y∗y ;
i f ( x>y && n<=N && n %12==11)
isprime [ n]=! isprime [ n ] ;
}
}
f o r ( int n = 5 ; n<=s q r t (N) ; ++n )
i f ( isprime [ n ] )
f o r ( int k = n∗n ; k<=N; k+=n∗n )
221
TEORÍA DE NÚMERO
i s p r i m e [ k]= f a l s e ;
i s p r i m e [ 2 ] = i s p r i m e [ 3 ] = true ;
}
3. R son las reglas que nos indican qué números y qué operaciones son
válidos en el sistema, y cuáles no. En un sistema de numeración po-
sicional las reglas son bastante simples, mientras que la numeración
romana requiere reglas algo más elaboradas.
222
TEORÍA DE NÚMERO
223
TEORÍA DE NÚMERO
return d i g i t s ;
}
Esta claro que cuando la base a convertir sea mayor que 10 se hace ne-
cesario otra función auxiliar que luego los valores 11, 12 , 13 a A, B, C y ası́
sucesivamente por ejemplo para el caso de la base hexadecimal.
También basados en autómatas de divisibilidad es posible determinar si
un numero expresado en una base X (debe sistemas de numeración pondera-
dos o posicionales) es divisible por un número es base decimal.
224
TEORÍA DE NÚMERO
int t S i z e=numero . s i z e ( ) ;
i f ( ! estado )
return true ;
else
return f a l s e ;
}
s t r i n g numberRoman ( int x )
{
s t r i n g roman ;
roman . c l e a r ( ) ;
while ( x>=1000){x−=1000;roman+="M" ; }
while ( x>=900){x−=900;roman+="CM" ; }
while ( x>=500){x−=500;roman+="D" ; }
while ( x>=400){x−=400;roman+="CD" ; }
while ( x>=100){x−=100;roman+="C" ; }
while ( x>=90){x−=90;roman+="XC" ; }
while ( x>=50){x−=50;roman+="L" ; }
while ( x>=40){x−=40;roman+="XL" ; }
while ( x>=10){x−=10;roman+="X" ; }
while ( x>=9){x−=9;roman+="IX" ; }
while ( x>=5){x−=5;roman+="V" ; }
while ( x>=4){x−=4;roman+="IV" ; }
while ( x>=1){x−=1;roman+="I" ; }
return roman ;
}
225
TEORÍA DE NÚMERO
226
TEORÍA DE NÚMERO
Primo 2 0 1 0 2 0 1 0 3 0 1 8
Primo 3 0 0 1 0 0 1 0 0 2 0 4
Para el caso del 2 seria todos los múltiplos de 2 hasta N ejemplo para 10
hay 5 que seria 10/2 luego todos los múltiplos de 4 hasta N que serı́a N/4
y ası́ hasta que la potencia de 2 sea mayor que N la suma de estos seria el
exponente de la potencia de 2 luego de descomponer N!, en este caso serı́a
210/2+10/4+10/8 =25+2+1 =28 y este serı́a el procedimiento para todos los primos
hasta N.
227
TEORÍA DE NÚMERO
2: Si termina en 0, 2, 4, 6 ó 8.
4: Si sus dos últimas cifras son 00 ó un múltiplo de 4 (12, 16, 20, 24,
28, 32, 36 y 40).
5: Si termina en 0 y 5.
228
TEORÍA DE NÚMERO
11: Si la suma de las cifras que ocupan un lugar par menos la suma de
las otras cifras es 0 ó un múltiplo de 11 (11, 22, 33, 44,. . . )
import j a v a . math . B i g I n t e g e r ;
import j a v a . u t i l . Scanner ;
public c l a s s Main {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
B i g I n t e g e r b=new B i g I n t e g e r ( "495" ) ;
229
TEORÍA DE NÚMERO
230
TEORÍA DE NÚMERO
int s i z e= number . s i z e ( ) ;
REP( i , s i z e )
s t a t e =( s t a t e ∗ b a s e+v a l u e ( number [ i ] ) ) % d e c i m a l ;
i f ( ! s t a t e ) return true ;
e l s e return f a l s e ;
}
231
TEORÍA DE NÚMERO
2842 - Lazy Cat Hacer una criba de primos hasta un millón guardando
en un vector auxiliar aquellos que también son palı́ndromos. El resto de
los primos palı́ndromos del rango de 106 a 107 se construye números que
sean palı́ndromos y verificar que sean primos en caso de cumplir con los
dos se guardan en el vector auxiliar. Se lee los lı́mites del intervalo se hace
un lower bound y upper bound con el vector de primos palı́ndromos y se
imprime la diferencias de los iteradores.
1936 - An Easy Problem! Para resolver este problema solo se tiene que
conocer que un número R expresado en una base numérica N es divisible por
X donde X >= N + 1 siempre y cuando la suma de los dı́gitos de R de un
múltiplo de X. Ojo el numero entrado puede ser negativo.
232
TEORÍA DE NÚMERO
1131 - Divisors Una vez leido el problema es evidente que nos piden
calcular el numero de divisores de Cn,p el cual a priori se calcula de la siguiente
forma:
n!
Cn,k = k!∗(n−k)!
Como vemos si calculamos primero el valor de Cn,p para luego determinar
la cantidad de divisores de ese número este proceso puede ser lento y existir el
desbordamiento de datos si el lenguaje escogido no soporte enteros grandes.
Ahora sabemos que la cantidad de divisores de número se puede calcular a
partir de la siguiente fórmula:
N = k1 ∗ q1 + k2 ∗ q2 + k3 ∗ q3 + ... + kn ∗ qn
Donde N es el número al cual deseamos conocer la cantidad de divisores
y el miembro derecho de la fómula es la factorización de N en los números
primos q1 , q2 , q3 y qn . Y k1 , k2 , k3 y kn la cantidad de veces que esta presente
el número primo asociado a este en la factorización. Luego la cantidad de
divisores de un número N es igual al resultado de la siguiente fómula:
(k1 + 1) ∗ (k2 + 1) ∗ (k3 + 1) ∗ .... ∗ (kn + 1)
Una vez visto esto vayamos a otro dellate de la siguiente ecuación:
n!
k!∗(n−k)!
Si la expandimos de la siguiente manera:
1∗2∗3∗...∗n
1∗2∗3∗...∗k∗1∗2∗3∗...(n−k)
Y comenzamos a simplificar los números del denominador con los núme-
ros presentes en numerador, la simplicación va dar como resultado en el
numerador todos los números primos menores e iguales a N mientras en el
denominador va estar todos los numeros primos menores e iguales que n-k
y todos los numeros primos menores e iguales que k. Tanto en denominador
como el numerador los numeros primos pueden estar repetidos.
233
TEORÍA DE NÚMERO
Una vez visto estos detalles la solución del problema se reduce a realizar
una criba de primos para determinar todos los primos desde 1 hasta 435.
Luego tener un vector de la misma dimensión del vector donde almacenaste
los primos que arrojó la criba. Este vector al cual llamaremos countPrimes al-
macenará en la posición e la cantidad de veces que el primo enesimo divide a
un determinado valor. Inicialmente los valores almacenados en countPrimes
serán cero luego factorizamos en numeros primos k sumando en countPri-
mes[e] la cantidad de veces que primes[e] divide a k. Luego factorizamos k-n
y n pero en vez de sumar en countPrimes[e] restaremos la cantidad de veces
que primes[e] divide a k-n y n. Muy importante primes[e] no tiene que ser
divisor de k , ni k-n y n. solo menor e igual que este y contamos la parte
entera
Q de la división. Luego la solución es:
(countP rimes[i] + 1)
Otra variante solución es serı́a descomponer los 3 términos n!, (n-k)! y
k! luego a la descomposición de n! le restamos los exponentes de los otros
2 términos y aplicar la fórmula para la cantidad de divisores de un valor N
visto en el epı́grafe Factores primos.
234
TEORÍA DE NÚMERO
3760 - PDF Check El ejercicio solo nos pide chequear si dado dos números
A y B todos los divisores propios de A sumados es igual a B o viceversa con
al menos uno de los dos casos se cumplan la respuesta es positiva sino es
negativo. Se puede tener un arreglo donde la inésima posición se va alamcenar
la suma de todos los divisores del inésimo valor. Para llenar el arreglo se puede
utilizar un algoritmo parecido a la criba de primos de Eratóstenes.
2874 - Lazy Cat III Este problema bien se puede solucionar al menos
con dos variantes, las cuales explicares. Para comenzar que dos números
son coprimos cuando el máximo común divisor de los dos es uno. Dicho
esto vamos solucionar el problema utilizando una técnica de programación
dinámica como es la tabla acumulativa. Primero creamos una matriz de 2 filas
con 105 +10 columnas todas con valor cero. Luego recorremos la matriz las
dos filas simultaneas de 1 a N y para cada columna i planteamos lo siguiente:
Si i es coprimo del numero de Garfield entonces
matriz[0][i]=1+matriz[0][i-1]
sino
matriz[0][i]=matriz[0][i-1]
De similar manera se plantea:
Si i es coprimo del numero de Anders entonces
matriz[1][i]=1+matriz[1][i-1]
sino
matriz[1][i]=matriz[1][i-1]
Luego para saber si Garfield respondió bien basta con probar que matriz[1][aj]-
matriz[1][ai-1] es igual al número dicho por Garfield. De igual manera suce-
de con Anders basta con chequear su respuesta coincide con matriz[0][gj]-
matriz[0][gi-1]. Luego de acuerdo a si aceptan o no en sus respuestas se va
llevando un acumulado de puntos para cada y luego se saca el resultado del
problema de acuerdo a los valores de esas variables.
La segunda variante es utilizando la estructura de datos Range Tree con
algunas modificaciones en su operación de construcción y consulta. Aquı́ les
dejo las modificaciones para este caso.
struct Node
{
int c C o p r i m e G a r f i e l d ;
int cCoprimeAnders ;
};
Node ST [MAXTREE] ;
....
235
TEORÍA DE NÚMERO
{
i f ( l e f t == r i g h t )
{
i f (GCD( min ( l e f t , numberAnders ) ,max( l e f t , numberAnders ) )
==1)
ST [ i d x ] . cCoprimeAnders =1;
else
ST [ i d x ] . cCoprimeAnders =0;
i f (GCD( min ( l e f t , n u m b e r G a r f i e l d ) ,max( l e f t ,
n u m b e r G a r f i e l d ) )==1)
ST [ i d x ] . c C o p r i m e G a r f i e l d =1;
else
ST [ i d x ] . c C o p r i m e G a r f i e l d =0;
return ;
}
I n i t T r e e ( 2 ∗ idx , l e f t , MID) ;
I n i t T r e e ( 2 ∗ i d x +1, MID+1, r i g h t ) ;
ST [ i d x ] . cCoprimeAnders = ST [ 2 ∗ i d x ] . cCoprimeAnders+ST [ 2 ∗
i d x + 1 ] . cCoprimeAnders ;
ST [ i d x ] . c C o p r i m e G a r f i e l d = ST [ 2 ∗ i d x ] . c C o p r i m e G a r f i e l d+ST
[2∗ idx +1]. cCoprimeGarfield ;
}
236
TEORÍA DE NÚMERO
1602 - Numerically Speaking Para solución del ejercicio solo basta saber
como llevar un número en base decimal a su representación númerica en base
26 teniendo en cuenta que 1 es a hasta 26 que es z y de forma contraria
237
TEORÍA DE NÚMERO
1288 - Div 6 La idea del ejercicio es bien sencilla dado una colección
de números debemos de cir de cada uno si es divisible o no por 6. La una
dificultad esta es que los valores númericos estan en el rango de de números
hasta 1000 dı́gitos. Es evidente que desea solucionar el problema debe tratar
el número como cadena (a no ser que use BigInteger de Java o Ptyhon).
Ahora como se puede saber si la cadena que representa un valor númerico es
divisible por 6. Bien sabemos que el 6 factorizado en numeros primos es 2 y
3. Esto significa que cualquier número divisible por 6 debe ser divisible por 3
y 2 a la vez. Como sabemos un número es divisble por 2 cuando termina su
ultima cifra en 0,2,4,6,8. Esto es fácil de comprobar en la cadena solo debemos
acceder a la ultima posición y chequear que valor tiene este. Ahora sabemos
que un número es divisible por 3 cuando la suma de los digitos que componen
el número es multiplo de 3. Esto lo podemos comprobar recorriendo la cadena
y cada posición y el caracter llevarlo a su valor en número y sumarlo y si dicha
suma es divisible por 3 entonces el valor númerico contenido en la cadena
es divisble por 3. Con lo explicado anteriormente ya la solución del ejercicio
queda mucho mas clara.
238
TEORÍA DE NÚMERO
1151 - Coprimes II Para saber cuántos números son coprimos con N des-
de 1 hasta A descomponemos N en factores primos y aplicamos una fórmula
de inclusión y exclusiones con una máscara de bit para saber cuántos núme-
ros comparten algún primo y si es ası́ este no es coprimo ejemplo para N=10
y A = 15 los que no son coprimos con 10 = 2*5 son 15/2 + 15/5 – 15/10
= 7+3-1 = 9 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 quedándonos ası́ que la
cantidad de coprimos con 10 desde 1 hasta 15 son 15-9 = 6 para un intervalo
[A,B] seria IE(B)-IE(A-1) siendo IE el método de Inclusión y exclusiones.
1996 - Big Divisor Otro ejemplo para trabajar con números descompues-
to en factores primos. En este caso dan un arreglo de multiplicaciones en el
denominador y otro en el denominador, descomponer cada uno de estos y los
exponentes del denominador restarlo al del numerador. Luego si el exponente
es positivo pertenece al numerador y si en negativo pertenece al denominador
y ası́ quedan ya simplificados.
239
TEORÍA DE NÚMERO
1284 – Last Digit Solo hay que darse cuenta que lo que te piden es la
n!
permutación con repeticiones de un conjunto de letras n1!∗n2!∗n3!∗...∗nk! donde
nk es la cantidad de veces que se repite el elemento k. luego de descomponer
en factores primos n! le restamos los exponentes de los nk! Y sobre el número
descompuesto que nos queda realizamos el siguiente análisis: en la Base 10
que números nos da un 0 al final, la multiplicación de un 5 por un 2 miramos
en la descomposición del número 2x y 5y luego nos quedamos con el mayor
de los dos exponente si x¿y nos quedamos con 2x−y y si no con 5y−x ejemplo
si tenemos un número que en su descomposición tienes 28 y 53 que es por
ejemplo 32000 si realizamos este análisis nos queda 25 solamente que es 32
lo mismo que el caso anterior pero sin los ceros. Luego de aplicar este análi-
sis multiplicamos el primo de la descomposición por el exponente y vamos
modulando por 10 para ası́ quedarnos con el último digito.
2228 - Math Dare El problema nos pide hallar para un N un K tal que sea
el menor posible pero que cumpla la siguiente condición: que cada divisor de N
no sea primo relativo con K. Para empezar vamos a explicar que dos números
se dicen que son primos relativos entre si solo si el máximo común divisor
entre los números es 1. Para solucionar el problema solo basta con factorizar
en números primos N y multiplicar todos números primos presentes una sola
vez es decir si un número primo en la factorización su exponente es mayor
que uno solo se debe tener en cuenta una vez. Como todo los divisores de N
menos el 1 en sus factorizaciones van a tener presente alguna combinación
de los números primos presentes en la factorización de N el K a buscar debe
tener en su factorización presente con exponente uno cada uno de los primos
que dividen a N para que cada máximo común divisor de K con algún divisor
de N arroje un número primo o un valor que es la multiplicación de varios
de estos. Tener en cuenta que cuando N es primo K es igual a N.
1102 - You Can Say 11 El ejercicio nos pide determinar si para un núme-
ro N determinar si el mismo es múltiplo de 11 o no. El único detalle es que
dicho número puede tener hasta 1000 digitos por lo que no podemos alama-
cenar dicho valor en variables de tipo númericas. Para resolver el problema
vamos apoyarnos en la regla de divisibilidad del número 11 la cual plantea
que un número es divisible por 11 si la diferencia de las sumas de los digitos
en posiciones pares e impares producen un número multiplo de 11. Para al-
macenar el número vamos a utilizar una variable de tipo string y para hallar
240
TEORÍA DE NÚMERO
las sumas basta con recorrer el string e ir sumando en dos variables sumado-
ras los dı́gitos que están en las posiciones pares y los dı́gitos que están en la
posiciones impares.
241
TEORÍA DE NÚMERO
242
Capı́tulo 10
Geometrı́a
243
GEOMETRÍA
Es evidente que esta solución tanto en peor como en el mejor de los casos
compara todos los puntos contra todos por lo que podemos decir que la
complejidad de este algoritmo es O(n2 ) siendo n la cantidad de puntos en el
plano. Por lo que solo es recomendable usar esta variante en problemas de
concursos cuando la cantidad de puntos no superen los 1000.
struct Po i nt
{
double X,Y;
};
double d i s t ( P oi nt p1 , P oi nt p2 )
{
return s q r t ( ( p1 . x − p2 . x ) ∗ ( p1 . x − p2 . x ) +(p1 . y − p2 . y )
∗ ( p1 . y − p2 . y ) ) ;
}
double c l o s e s t P a i r V 1 ( Po in t P [ ] , int n )
{
double minDistance = FLT MAX;
f o r ( int i = 0 ; i < n ; ++i )
f o r ( int j = i +1; j < n ; ++j )
i f ( d i s t (P [ i ] , P [ j ] ) < minDistance )
minDistance = d i s t (P [ i ] , P [ j ] ) ;
return minDistance ;
}
244
GEOMETRÍA
245
GEOMETRÍA
esto puede hacerse suponiendo que los puntos de P’ están ordenados por la
coordenada y, luego tendremos que resolver como ordenar los puntos.
246
GEOMETRÍA
struct Po i nt
{
double X,Y;
};
double d i s t ( P oi nt p1 , P oi nt p2 )
{
return s q r t ( ( p1 .X − p2 .X) ∗ ( p1 .X − p2 .X) +(p1 .Y − p2 .Y)
∗ ( p1 .Y − p2 .Y) ) ;
}
double c l o s e s t P a i r V 1 ( Po in t P [ ] , int n )
{
double minDistance = FLT MAX;
f o r ( int i = 0 ; i < n ; ++i )
f o r ( int j = i +1; j < n ; ++j )
i f ( d i s t (P [ i ] , P [ j ] ) < minDistance )
247
GEOMETRÍA
minDistance = d i s t (P [ i ] , P [ j ] ) ;
return minDistance ;
}
double c l o s e s t U t i l ( P oi nt P [ ] , int n )
{
i f ( n <= 3 )
return c l o s e s t P a i r V 1 (P , n ) ;
int mid = n / 2 ;
Po i nt midPoint = P [ mid ] ;
double d l = c l o s e s t U t i l (P , mid ) ;
double dr = c l o s e s t U t i l (P + mid , n−mid ) ;
double d = min ( dl , dr ) ;
Po i nt s t r i p [ n ] ;
int j = 0 ;
f o r ( int i = 0 ; i < n ; i ++)
i f ( abs (P [ i ] . X − midPoint .X) < d )
{
s t r i p [ j ] = P[ i ] ;
j ++;
}
return min ( d , s t r i p C l o s e s t ( s t r i p , j , d ) ) ;
}
double c l o s e s t P a i r V 2 ( Po in t P [ ] , int n )
{
q s o r t (P , n , s i z e o f ( P oi nt ) , compareX ) ;
return c l o s e s t U t i l (P , n ) ;
}
248
GEOMETRÍA
249
GEOMETRÍA
struct Po i nt
{
double X,Y;
Po i nt ( double X=0,double Y=0)
{
X= X ;
Y= Y ;
}
void r e a d P o i n t ( )
{
c i n >>X>>Y;
}
};
double d i s t a n c e P o i n t T o P o i n t ( Po in t a , Po in t b )
{
double d i s t =( a . X− b .X) ∗ ( a . X− b .X) +( a . Y− b .Y) ∗ ( a . Y− b
.Y) ;
return s q r t ( d i s t ) ;
}
double c l o s e s t P a i r W i t h L i n e S w e e p ( P oi nt p o i n t s [ ] , int n p o i n t )
{
s o r t ( p o i n t s +1, p o i n t s+ n p o i n t +1) ;
i f ( p o i n t s == 0 )
return 0 ;
double d i s t = INF ; /∗ INF debe s e r un v a l o r que s e a mayor
que l a d i s t a n c i a maxima
en que s e pueda e n c o n t r a r dos puntos segun e l problema .
S i d i c e n que l a s c o o r d e n a d a s de l o s puntos va e s t a r
e n t r e −10ˆ9 a 10ˆ9 e n t o n c e s :
INF= d i s t a n c e P o i n t T o P o i n t ( P oi nt ( −10ˆ9 , −10ˆ9) , P o in t
( 1 0 ˆ 9 , 1 0 ˆ 9 ) ) +100
250
GEOMETRÍA
∗/
int t B e g i n = 1 , tEnd = 1 , t o t = 1 ;
return d i s t ;
}
251
GEOMETRÍA
252
GEOMETRÍA
(a) Sea pi =(Xi ,Yi ). Selecionar el bucket [Iδ (Xi ), Iδ (Yi )] y sus buckets
vecinos de la grilla.
(b) Si no ay puntos en ninguno de estos buckets entonces continuar
el ciclo. Sino computar la distancia minima y los (a lo suma 36)
puntos encontrados en los buckets. Sea δ 0 esta nueva distancia
(c) Si δ 0 < δ entonces δ = δ 0 destruir la grilla anterior y construir una
nueva grilla a partir de Pi = p1 , ..., pi . Sino simplemente agregar
pi a la grilla.
3. Devolver δ.
253
GEOMETRÍA
254
GEOMETRÍA
Puede ser usada para aproximar formas más complejas (cubiertas con-
vexas de polı́gonos o poliedros)
donde 0 ≤ α i ≤ 1 y
255
GEOMETRÍA
Siempre ocurre que el punto más bajo de tangencia pi es tal que q está
a la izquierda de pi−1 pi pero a la derecha de pi pi+1 .
256
GEOMETRÍA
Orden de ejecución:
Cada uno de los n-3 puntos que añaden necesitan calcular las dos tan-
gentes, operación lineal en el peor de los casos.
257
GEOMETRÍA
4. Agregar p1 y p2 a H.
5. i ← 2
6. mientras p1 6= pi :
7. Retornar H
258
GEOMETRÍA
259
GEOMETRÍA
QuickHull(a, b, S):
1. Si S=∅ retornar ∅
3. Sino
260
GEOMETRÍA
261
GEOMETRÍA
el tiempo necesario para realizar todos los pasos del algoritmo, ignorando las
llamadas recursivas Esto incluye el tiempo para:
262
GEOMETRÍA
Regresar ab
263
GEOMETRÍA
264
GEOMETRÍA
struct Po i nt
{
double X, Y;
P oi nt ( )
{
265
GEOMETRÍA
this−>X = this−>Y = 0 ;
}
P oi nt ( double x , double y )
{
this−>X = x;
this−>Y = y;
}
};
int n ;
P oi nt P [MAX N ] ;
P oi nt R;
i n l i n e bool cmp( Po in t A, P oi nt B)
{
return atan2 (A. Y−R. Y,A. X−R.X) < atan2 (B . Y−R. Y, B . X−R.X) ;
}
swap (P [ 1 ] , P [ m i n i d ] ) ;
R = P[1];
s o r t (P+2, P+n+1, cmp) ;
P[ 0 ] = P[ n ] ;
int H u l l S i z e = 1 ;
266
GEOMETRÍA
}
swap (P[++ H u l l S i z e ] , P [ i ] ) ;
}
int main ( )
{
n = 4;
P[1] = Po in t ( 4 , 8) ;
P[2] = Po in t ( 4 , 12) ;
P[3] = Po in t ( 5 , 9.3) ;
P[4] = Po in t ( 7 , 8) ;
v e c t o r <Point> cH ;
int m = GrahamScan ( cH ) ;
using namespace s t d ;
struct pt {
double x , y ;
};
bool cmp ( pt a , pt b ) {
return a . x<b . x | | a . x==b . x && a . y<b . y ;
}
bool cw ( pt a , pt b , pt c ) {
return a . x ∗ ( b . y−c . y ) + b . x ∗ ( c . y−a . y ) + c . x ∗ ( a . y−b . y )
<0;
}
bool ccw ( pt a , pt b , pt c ) {
return a . x ∗ ( b . y−c . y ) + b . x ∗ ( c . y−a . y ) + c . x ∗ ( a . y−b . y )>
0;
}
267
GEOMETRÍA
#define Po i nt complex<double>
namespace s t d {
bool operator <(const Po i nt & a , const Po i nt & b ) {
return r e a l ( a ) != r e a l ( b ) ? r e a l ( a )<r e a l ( b ) : imag ( a )<imag ( b ) ;
}
}
268
GEOMETRÍA
int ccw ( P oi nt a , Po in t b , Po in t c ) {
b −= a ; c −= a ;
i f ( c r o s s ( b , c )> 0 ) return +1; // counter clockwise
i f ( c r o s s ( b , c ) <0) return −1; // clockwise
i f ( dot ( b , c ) <0) return +2; // c − a − b on l i n e
i f ( norm ( b ) <norm ( c ) ) return −2; // a − b − c on l i n e
return 0 ;
}
v e c t o r <Point> c o n v e x H u l l ( v e c t o r <Point> ps ) {
int n = ps . s i z e ( ) , k = 0 ;
s o r t ( ps . b e g i n ( ) , ps . end ( ) ) ;
v e c t o r <Point> ch ( 2 ∗ n ) ;
f o r ( int i =0; i <n ; ch [ k++]=ps [ i ++]) // lower −h u l l
while ( k >= 2 && ccw ( ch [ k −2] , ch [ k −1] , ps [ i ] ) <=0) −−k ;
f o r ( int i = n−2, t = k + 1 ; i >=0; ch [ k++] = ps [ i −−]) //
upper−h u l l
while ( k >= t && ccw ( ch [ k −2] , ch [ k −1] , ps [ i ] )<= 0 ) −−k ;
ch . r e s i z e ( k−1) ;
return ch ;
}
Esta implementación asume que el vector que contiene todos los puntos
y se le pasa como parámetro a la función tiene al menos 3 puntos.
Intersección de segmentos.
Intersección de lı́neas
269
GEOMETRÍA
Lı́nea: Una lı́nea funciona como una sucesión continua de puntos trazados,
como por ejemplo un trazo o un guion. En geometrı́a euclidiana, la recta o
la lı́nea recta se extiende en una misma dirección por tanto tiene una sola
dimensión y contiene infinitos puntos; se puede considerar que está compuesta
de infinitos segmentos. Dicha recta también se puede describir como una
sucesión continua e indefinida de puntos extendidos en una sola dimensión,
es decir, no posee principio ni fin.
Ambos se pueden definir con dos puntos distintos entre sı́ con la diferencia
que el primero esta acotado al intervalo que define dichos puntos mientras la
segunda pueden extenderse mas alla del intervalo definido por los puntos.
Una vez definida las condiciones que harı́an que los segmentos A y C se
intersecten, nos podemos percatar que que para primera cuatro condiciones
son identicas solo cambian los puntos o su orden. Mientras en la quinta se
comprueba en un sentido y luego en el otro. Es por eso que vamos centrarnos
en como determinar:
270
GEOMETRÍA
Dados tres puntos saber son colineales . Dos puntos siempre van ser
colineales porque es la mı́nima cantidad de puntos para definir un lı́nea.
Ahora tres o más puntos van ser colineales si se es capaz de trazar una lı́nea
recta entre los dos puntos extremos y que pase por el resto de los puntos.
Para determinar si los puntos e, f y g son colineales vamos construir
→
− →
−
dos vectores F y G que tengan como punto de origen el punto e y como
de destino los puntos f y g respectivamente. Sin importar las posiciones de
los puntos el ángulo entre los dos vectores simepre va estar en el rango de
0 a 180 grados. Precisamente con esa amplitudes serı́a los casos en que los
puntos serı́an colineales. Bueno entonces queda ver como hallar el ángulo
entre dos vectores y luego ver si esa amplitud es 0 ó 180 entonces los puntos
son colineales.
Con la operación binaria producto vectorial o producto cruz entre dos
vectores es posible determinar el valor del seno del ángulo comprendido entre
los vectores segun la siguiente expresión:
→
− → − →
− →−
F x G = (| F || G | sin θ)n̂
→
− →
−
donde n̂ es el vector unitario y ortogonal a los vectores F y G y su
dirección está dada por la regla de la mano derecha y θ es, como antes, el
ángulo entre a y b. A la regla de la mano derecha se la llama a menudo
también regla del sacacorchos. El valor de θ para nuestro caso será 0 o 180
(0 o π) y en ambos caso la expresión sin θ se hace cero y esto produce que el
miembro derecho de la
expresión sea cero.
→
− → −
F xG = 0
Esto conduce a plantaer que tres puntos son colineales si el producto
vectorial o cruz entre dos de los vectores que se construir con ellos es igual
cero. Desarrollando el miembro izquierdo de la expresión:
((P ex − P fx ) ∗ (P gy − P fy ) − (P ey − P fy ) ∗ (P gx − P fx )) = 0
271
GEOMETRÍA
Donde min y max son funciones que devuelven el mı́nimo y máximo res-
pectivamente entre los valores pasado por argumentos.
struct Po i nt
{
double X, Y;
Po i nt ( double x , double y )
{
this−>X = x ;
this−>Y = y ;
}
};
i n l i n e double c r o s s P r o d u c t ( P oi nt a , Po in t b , P oi nt c )
{
return ( ( b .X − a .X) ∗ ( c .Y − a .Y) − ( b .Y − a .Y) ∗ ( c .X − a .X) )
;
272
GEOMETRÍA
i n l i n e bool i s L e f t ( P oi nt a , Po in t b , P o in t c )
{
return ( c r o s s P r o d u c t ( a , b , c ) > 0 ) ;
}
i n l i n e bool i s C o l l i n e a r ( P oi nt a , Po in t b , P oi nt c )
{
return ( c r o s s P r o d u c t ( a , b , c ) == 0 ) ;
}
i n l i n e bool o p p o s i t e S i d e s ( Po in t a , Po i nt b , Po in t c , Po in t d )
{
return ( i s L e f t ( a , b , c ) != i s L e f t ( a , b , d ) ) ;
}
i n l i n e bool i s B e t w e e n ( P oi nt a , Po in t b , Po in t c )
{
return ( min ( a . X, b .X) <= c .X && c .X <= max( a . X, b .X) && min ( a .
Y, b .Y) <= c .Y && c .Y <= max( a . Y, b .Y) ) ;
}
i n l i n e bool i n t e r s e c t ( P oi nt a , Po in t b , P oi nt c , P oi nt d )
{
i f ( i s C o l l i n e a r ( a , b , c ) && i s B e t w e e n ( a , b , c ) ) return true ;
i f ( i s C o l l i n e a r ( a , b , d ) && i s B e t w e e n ( a , b , d ) ) return true ;
i f ( i s C o l l i n e a r ( c , d , a ) && i s B e t w e e n ( c , d , a ) ) return true ;
i f ( i s C o l l i n e a r ( c , d , b ) && i s B e t w e e n ( c , d , b ) ) return true ;
return ( o p p o s i t e S i d e s ( a , b , c , d ) && o p p o s i t e S i d e s ( c , d , a ,
b) ) ;
}
int main ( )
{
P oi nt A( 0 . 0 , 0 . 0 ) , B( 0 . 0 , 2 . 0 ) , C( −1.0 , 2 . 0 ) , D( 1 . 0 , 2 . 0 ) ;
p r i n t f ( i n t e r s e c t (A, B, C,D) ? "YES" : "NO" ) ;
p r i n t f ( "\n" ) ;
return 0 ;
}
273
GEOMETRÍA
274
GEOMETRÍA
Como se puede observar en la figura que los centros de las tres circun-
ferencia (A,B,C) son puntos periféricos de las otras dos circunferencias. Por
tanto las longitudes de AB, AC y BC son iguales al radio el cual como todos
conocemos casi siempre es la mitad del diámetro que es el único dato inicial
que da el problema. Por tanto AB es igual AC y BC por lo que el triángulo
ABC es equilátero. La solución del problema es X + Y donde X es área verde
mientras Y es igual al área amarrilla. Bien el área verde es igual al área de un
triangulo equilátero conocido su lado. Si tomamos la circunferencia de centro
C vemos que con los puntos A y B se forma un sector circular cuyo ángulo es
60(el ángulo del sector circular coincide con el equilátero), teniendo el radio
y el ángulo es fácil calcular el área del sector circular y a este resultado se le
resta el área del triángulo equilátero se tendrá un tercio del área amarrilla.
Solo tenemos que multiplicar ese resultado por tres para tener el área ama-
rilla. Una vez visto lo principales aspectos a tener en cuenta en la solución
y haciendo los despejes y agrupaciones pertinentes la solución queda en la
siguiente expresión:
276
GEOMETRÍA
√
√ radio∗radio∗(((1,00000/4,00000)∗ 3,0000)+(π/2,0000)−((3,00000/4,00000)∗
3,0000))
277
GEOMETRÍA
2922 - Euclid Para resolver este problema se debe seguir los siguientes
pasos:
278
GEOMETRÍA
279
GEOMETRÍA
3679 - Are You Ok? La solución del ejercicio radica en dado dos colec-
ciones de puntos la primera conforma un polı́gono mientras de la segunda se
necesita saber cuantos de esos puntos estan dentro del polı́gono. Solo se ne-
cesita aplicar el algoritmo que determina la posición (dentro, en el perimetro
o fuera) de un punto con respecto a un polı́gono, cada punto de la segunda
colección.
3427 - Lost in the Forest Para la solucion de este problema solo se debe
recorrer la coleccion de puntos dados en el orden de entrada y analizar para
cada triada de puntos i-1, i y i+1 hacia donde abre el angulo que forman. En
caso de los puntos sean colineales no se debe imprimir nada.
3761 - Shortest Path Para explicar este problema primero vamos des-
glosar en la diferentes situaciones o casos en que se puede encontrar en este
problema para eso vamos a utilizar la siguiente figura:
280
GEOMETRÍA
3936 - Shaded Area Ejercicio muy sencillo, solo basta con plantear la
suma de las áreas del rectángulo y el triangulo, con las dimensiones descritas
en el problema.
281
GEOMETRÍA
Esta claro que necesitamos determinar bien las longitudes de los lados
del triángulo o dos sus lados de este con la amplitud del ángulo comprendido
para determinar el área del triángulo.
Vamos a comenzar por calcular la longitud de los arcos a,b y c. Vamos
a definir la logitud de la circunferencia de centro O como L que va ser igual
como sabemos a 2πR. El problema nos plantea que los arcos tienen deter-
minada proporción con respecto a a la longitud de la circuferencia. Esto nos
permite plantaer lo siguiente:
a = 3x
b = 4x
c = 5x
a+b+c=L
3x + 4x + 5x = L
3x + 4x + 5x = 2πR
Donde x es el factor de proporción, despejando y calculando x en la ultima
ecuación planteada podemos determinar luego las longitudes de los arcos a,b
282
GEOMETRÍA
y c. Una vez calculada las longitudes de los arcos podemos determinar las
amplitudes de los ángulos corespondientes (Alfa, Beta y Sigma) a dichos
arcos.
Los ángulos 6 CDO, 6 ADO, 6 AF O, 6 BF O, 6 CEO y 6 AEO, son rectos
o con una amplitud de 900 grado por ser los segmentos AC , AB y BC
tangentes a la circuferencia en los puntos D , F y E respectivamente.
En un cuadrilatero los ángulos interiores suman 3600 grados. Conociendo
esto es posible calcular los valores de los ángulos 6 M iu, 6 Lambda y 6 DAF
por ser los cuartos ángulos de los cuadrilateros ADOF, BEOF y BEOF respec-
tivamente de los cuales ya se conocen la amplitud de los tres restantes ángulos
que lo componen. Una vez determinado la amplitud del ángulo 6 DAF ve-
mos que independientemente de la longitud del radio de la circuferencia las
tangentes van a conformar un triángulo rectángulo en el ángulo que se opone
al arco a.
Este dato es importante porque la suma de la hipotemusa y el dia-
metro de una circuferencia inscrito en el triángulo rectángulo es
igual a la suma de sus catetos. Dicho esto podemos plantaer que:
2R + h = e + d(1)
Donde h , e y d son las longitudes de los segmentos BC , AB y AC
respectivamente. De igual manera se puede plantear que:
d = sin(Lambda) ∗ h(2)
e = sin(M iu) ∗ h(3)
Sustituyendo (2) y (3) en (1) y despejando h queda que:
2R + h = sin(M iu) ∗ h + sin(Lambda) ∗ h
2R = sin(M iu) ∗ h + sin(Lambda) ∗ h − h
2R = h(sin(M iu) + sin(Lambda) − 1)
2R
(sin(M iu)+sin(Lambda)−1)
=h
Una vez hallado el valor de h se determina los valores de e y d. Luego con
esos datos es suficiente para hallar el área del 4ABC que era lo pedido en el
problema. Como sugerencia trabajen los ángulos en radianes. El otro detalle
del problema es que la solución debe ser impresa con cuatro lugares despues
de la coma.
283
GEOMETRÍA
equilatero es igual
√ a:
2 3
area = a 4
Y como el hexágono regular esta compuesto por seis triángulos equiláteros
el área del hexágono es igual a la ecuación anterior por seis. Simplificando
nos queda la siguiente
√ función solución:
2 3
area = 3a 2
El otro detalle del problema es que la solución debe imprimirse con dos
lugares decimales.
1554 - Convex Hull Finding Si leemos el tı́tulo del problema ası́ como
su descripción nos podemos percatar que ambos se ajustan y que es evidente
que el problema se resuelve aplicando el algoritmo para hallar la cubierta
convexa mı́nima que encierra a todos los puntos en una dimensión 2D. El
único detalle del ejercicio es la salida del mismo.
284
GEOMETRÍA
πL2
S = L2 −
4
π
S = L2 (1 −)
4
Tener en cuenta que se debe imprimir dos lugares decimales.
285
GEOMETRÍA
286
Capı́tulo 11
Programación dinámica
287
PROGRAMACIÓN DINÁMICA
que no nos puede entregar la cantidad N solicitada por que no tiene como dar
esa cantidad bien sea porque con la denominaciones de los billetes con que
cuenta el cajero no puede conformar la cantidad requerida o N es tan grande
que con todo los billetes disponibles en el cajero no alcanza la cifra solicita-
da. La segunda es una cantidad de billetes que sumados dan la cantidad N
solicitada inicialmente.
La primera variante del coin change no servirá para determinar de cuantas
formas podemos devolver un valor N usando una serie de K denominaciones
de billetes. En otras palabras de cuanta formas podemos devolver 13 pesos
teniendo una cantidad infinita de billetes con los valores de 1, 3, 5, 10, 15.
Lo primeros que vamos a tener es una estructura lineal con una capacidad
máxima del máximo N que me pueden pedir que calcule en dicha estructura
voy almacenar en cada posición x la cantidad de maneras que se puede de-
volver ese valor x que indica posición. Esta estructura lineal sera una arreglo
que denominaremos way.
Inicialmente este arreglo sera rellenado con cero cada posición , excepto
la posición 0 la cual su valor será 1. Una vez planteado solo queda recorrer
cada posición para cada denominación de billetes y plantear los siguiente
way[j+monedas[i]]+=way[j] siempre y cuando j+monedas[i] sea menor que
N máximo. Si estoy en la posición 8 con el billete 5 significa que las formas
de pagar 13(8 + 5) va hacer la cantidad que este tenia mas la cantidad de
forma que yo puedo pagar 8 porque cada forma de 8 yo le sumo 5 y da 13.
El código quedarı́a de la siguiente manera:
288
PROGRAMACIÓN DINÁMICA
ponibles y su valor será 1 (Algo lógico para devolver 5 pesos solo tengo que
usar un billete porque entre denominaciones esta el valor 5). Luego para ca-
da posición que se cumpla que way[i+denominacion[j]]>way[i]+1 actualizo
way[i+denominacion[j]]=way[i]+1 ejemplo si para devolver 13 tengo que usar
5 billetes pero si para 8 tengo que utilizar 2 y con un billete de 5 ahora podrı́a
usar solo 3 billetes para 13 (dos billetes para que sumen 8 y un billete de 5).
Quedando el código de la siguiente manera.
289
PROGRAMACIÓN DINÁMICA
de esos tres tipos, necesarias para transformar u en v y cuáles son esas ope-
raciones, estudiando su complejidad en función de las longitudes de u y v.
En primer lugar, la transformación mostrada arriba no es óptima ya que
podemos pasar de abbac a abcbc en sólo dos pasos:
abbac −→ abcac (cambiamos b en la posición 3 por c)
−→ abcbc (cambiamos a en la posición 4 por c)
Llamaremos m a la longitud de la cadena u, n a la longitud de la ca-
dena v, y OB(m,n) indicará el número de operaciones básicas mı́nimo para
transformar una cadena u de longitud m en otra cadena v de longitud n.
Para resolver el problema utilizando Programación Dinámica es necesa-
rio plantearlo como una sucesión de decisiones que satisfaga el principio de
óptimo.
Para plantearla, vamos a fijarnos en el último elemento de cada una de las
cadenas. Si los dos son iguales, entonces tendremos que calcular el número
de operaciones básicas necesarias para obtener de la primera cadena menos
el último elemento, y la segunda cadena también sin el último elemento, es
decir,
OB(m, n) = OB(m − 1, n − 1) si um = vn Pero si los últimos elementos
fueran distintos habrı́a que escoger la situación más beneficiosa de entre tres
posibles: (i) considerar la primera cadena y la segunda pero sin el último
elemento, o bien (ii) la primera cadena menos el último elemento y la segunda
cadena, o bien (iii) las dos cadenas sin el último elemento. Esto da lugar a
la siguiente relación en recurrencia para OB(m,n) para este caso:
OB(m, n) = 1 + M in{OB(m, n − 1), OB(m − 1, n), OB(m − 1, n − 1)}
Donde:
m 6= 0, n 6= 0y um 6= vn
En cuanto a las condiciones iniciales, tenemos las tres siguientes:
OB(0, 0) = 0, OB(m, 0) = m y OB(0, n) = n
Una vez disponemos de la ecuación en recurrencia necesitamos resolverla
utilizando alguna estructura que nos permita reutilizar resultados interme-
dios, para esto utilizaremos una matriz con m filas y n columnas, a continua-
ción se muestra el código.
unsigned int e d i t d i s t a n c e ( s t r i n g s1 , s t r i n g s 2 )
{
const s i z e t l e n 1 = s 1 . s i z e ( ) , l e n 2 = s 2 . s i z e ( ) ;
v e c t o r < v e c t o r < unsigned int > > d ( l e n 1 + 1 , v e c t o r <
unsigned int > ( l e n 2 + 1 ) ) ;
290
PROGRAMACIÓN DINÁMICA
d [ 0 ] [ 0 ] = 0;
Como el algoritmo se limita a dos bucles anidados que sólo incluyen ope-
raciones constantes la complejidad de este algoritmo es de orden O(mn).
El edit distance entre dos cadenas está definido como el número mı́nimo de
operaciones para convertir una cadena en otra con tres operaciones, inserción,
eliminación y reemplazo.
Note que d[i-1][j]+1 representa un costo de 1 para la inserción, d[i][j-1]+1
costo 1 para eliminación, y d[i-1][j-1]+(s1[i-1] == s2[j-1] 0 : 1)) representa
costo 1 para reemplazo (en caso de que no sean iguales). Con estas conside-
raciones es fácil adaptar este problema a otros similares.
291
PROGRAMACIÓN DINÁMICA
292
PROGRAMACIÓN DINÁMICA
293
PROGRAMACIÓN DINÁMICA
using namespace s t d ;
typedef long long l l d ;
int n , m;
s t r i n g A, B ;
int dp [MAX N ] [ MAX N ] ;
i n l i n e int LCS ( )
{
f o r ( int i =0; i<=n ; i ++) dp [ i ] [ 0 ] = 0 ;
f o r ( int j =0; j<=m; j ++) dp [ 0 ] [ j ] = 0 ;
f o r ( int i =1; i<=n ; i ++)
{
f o r ( int j =1; j<=m; j ++)
{
i f (A[ i −1] == B [ j −1])
{
dp [ i ] [ j ] = dp [ i − 1 ] [ j −1] + 1 ;
}
else
{
dp [ i ] [ j ] = max( dp [ i ] [ j −1] , dp [ i − 1 ] [ j ] ) ;
}
}
}
return dp [ n ] [m] ;
}
i n l i n e s t r i n g getLCS ( )
{
string ret ;
s t a c k <char> S ;
int i i = n , j j = m;
while ( i i != 0 && j j != 0 )
{
i f (A[ i i −1] == B [ j j −1])
{
S . push (A[ i i −1]) ;
i i −−; j j −−;
}
e l s e i f ( dp [ i i − 1 ] [ j j ] > dp [ i i ] [ j j −1])
i i −−;
else
j j −−;
}
while ( ! S . empty ( ) )
{
r e t += S . top ( ) ;
294
PROGRAMACIÓN DINÁMICA
S . pop ( ) ;
}
return r e t ;
}
int main ( )
{
n = 5 , m = 6;
A = " aleks " ;
B = " abcdef " ;
p r i n t f ( " %d\n" ,LCS ( ) ) ;
p r i n t f ( " %s\n" , getLCS ( ) . c s t r ( ) ) ;
return 0 ;
}
(k)
Siendo Ij el número de formas posibles de poner k elementos en donde
hay j sı́mbolos “=”(es decir, k-j-1 sı́mbolos distintos), con 0 ≤ j <k, 1 ≤ k ≤
n. Vamos a tratar de expresar Ck en función de Ck−1 . Para ello, supongamos
que ya tenemos
y añadimos un nuevo elemento. Pueden ocurrir dos casos: que sea distinto
a todos los k–1 elementos anteriores, o bien que sea igual a uno de ellos.
Entonces, la expresión de Ck va a venir dada por:
295
PROGRAMACIÓN DINÁMICA
(k−1)
Con esto, tenemos Ck en función de los Ij , es decir, de los componentes
del caso anterior. Ahora bien, es posible también relacionar los I(k) con los
I(k−1) siguiente manera:
(k) (k−1)
I0 = kI0
(k) (k−1) (k−1)
I1 =(k-1) I1 +(k-1)I0
(k) (k−1) (k−1)
I2 =(k-2)I2 +(k-2)I1
...
(k) (k−1) (k−1)
Ik−2 = 2Ik−2 + 2Ik−3
(k) (k−1)
Ik−1 = Ik−2
(2) (2)
Cuyas condiciones iniciales son I0 =2 , I1 = 1 . Esto también puede
expresarse como sigue:
(j) (k−1) (k−1)
Ik =(k-j)(Ij +Ij−1 ) para 0 ≤ j ≤ k-1 y 2 ≤ k ≤n
(2)
I0 = 2
(2)
I1 = 1
(k)
I−1 = 0
(k)
Ik = 0
(j)
Ası́, el problema puede resolverse calculando cada I n (0 ≤ j ≤ n–1),
para finalmente calcular Cn mediante la expresión:
ULL I [MAX] ;
ULL s o l v e ( int n )
{
i f ( n<=1)
return n ;
296
PROGRAMACIÓN DINÁMICA
I [ i ]=0;
I [0]=1;
ULL y , x=0;
297
PROGRAMACIÓN DINÁMICA
298
PROGRAMACIÓN DINÁMICA
public c l a s s Knapsack
{
public s t a t i c void main ( S t r i n g [ ] a r g s )
{
int N = I n t e g e r . p a r s e I n t ( a r g s [ 0 ] ) ; // number o f i t e m s
int W = I n t e g e r . p a r s e I n t ( a r g s [ 1 ] ) ; // maximum w e i g h t o f
knapsack
int [ ] p r o f i t = new int [N + 1 ] ;
int [ ] w e i g h t = new int [N + 1 ] ;
// g e n e r a t e random i n s t a n c e , i t e m s 1 . . N
f o r ( int n = 1 ; n <= N; n++)
{
p r o f i t [ n ] = ( int ) ( Math . random ( ) ∗ 1 0 0 0 ) ;
w e i g h t [ n ] = ( int ) ( Math . random ( ) ∗ W) ;
}
// opt [ n ] [ w ] = max p r o f i t o f
// p a c k i n g i t e m s 1 . . n with w e i g h t l i m i t w
// s o l [ n ] [ w ] = d o e s opt s o l u t i o n t o pack
// i t e m s 1 . . n with w e i g h t
// l i m i t w i n c l u d e item n?
299
PROGRAMACIÓN DINÁMICA
}
else
{
take [ n ] = false ;
}
}
// p r i n t r e s u l t s
System . out . p r i n t l n ( "item" + "\t" + " profit " + "\t" + " weight
" + "\t" + "take" ) ;
f o r ( int n = 1 ; n <= N; n++)
{
System . out . p r i n t l n ( n + "\t" + p r o f i t [ n ] + "\t" + w e i g h t [ n
] + "\t" + t a k e [ n ] ) ;
}
}
}
int n , c a p a c i t y ;
int Weight [ 1 0 1 ] , Value [ 1 0 1 ] , S o l [ 1 0 0 1 ] ;
i n l i n e int Knapsack01 ( )
{
f o r ( int i =0; i<=c a p a c i t y ; i ++) S o l [ i ] = 0 ;
f o r ( int i =0; i <n ; i ++)
{
f o r ( int j=c a p a c i t y ; j >=1; j −−)
{
i f ( Weight [ i ] <= j )
{
int x = S o l [ j ] ;
300
PROGRAMACIÓN DINÁMICA
int main ( )
{
n = 4 , capacity = 6;
Weight [ 0 ] = 1 , Value [ 0 ] = 4 ;
Weight [ 1 ] = 2 , Value [ 1 ] = 6 ;
Weight [ 2 ] = 3 , Value [ 2 ] = 1 2 ;
Weight [ 3 ] = 2 , Value [ 3 ] = 7 ;
p r i n t f ( " %d\n" , Knapsack01 ( ) ) ;
return 0 ;
}
301
PROGRAMACIÓN DINÁMICA
Introducir una unidad más del objeto i lo cual indica que el valor de
V(i,p) será el resultado obtenido para V(i,p–pi ) más el valor del objeto
vi , con lo cual se verifica que V(i,p) = V(i,p–pi ) + bi .
int n , c a p a c i t y ;
int Weight [ 1 0 1 ] , Value [ 1 0 1 ] , S o l [ 1 0 0 1 ] ;
i n l i n e int Knapsack ( )
{
Sol [ 0 ] = 0;
f o r ( int i =1; i<=c a p a c i t y ; i ++)
{
int maks = 0 ;
302
PROGRAMACIÓN DINÁMICA
int t e k ;
f o r ( int j =0; j <n ; j ++)
{
i f ( Weight [ j ] <= i )
{
t e k = Value [ j ] + S o l [ i −Weight [ j ] ] ;
i f ( t e k > maks ) maks = t e k ;
}
}
S o l [ i ] = maks ;
}
return S o l [ c a p a c i t y ] ;
}
int main ( )
{
n = 5 , capacity = 17;
Weight [ 0 ] = 3 , Value [ 0 ] = 4 ;
Weight [ 1 ] = 4 , Value [ 1 ] = 5 ;
Weight [ 2 ] = 7 , Value [ 2 ] = 1 0 ;
Weight [ 3 ] = 8 , Value [ 3 ] = 1 1 ;
Weight [ 4 ] = 9 , Value [ 4 ] = 1 3 ;
p r i n t f ( " %d\n" , Knapsack ( ) ) ;
return 0 ;
}
303
PROGRAMACIÓN DINÁMICA
304
PROGRAMACIÓN DINÁMICA
305
PROGRAMACIÓN DINÁMICA
306
PROGRAMACIÓN DINÁMICA
int n;
int p [MAX N ] ;
int m[MAX N ] [ MAX N ] ;
int memo [MAX N ] [ MAX N ] ;
int main ( )
{
n = 6;
p [ 0 ] = 30 , p [ 1 ] = 35 , p [ 2 ] = 15 , p [ 3 ] = 5 , p [ 4 ] = 10 , p [ 5 ] =
20 , p [ 6 ] = 25;
p r i n t f ( " %d\n" , M a t r i x C h a i n M u l t i p l i c a t i o n ( 1 , 6 ) ) ;
return 0 ;
}
307
PROGRAMACIÓN DINÁMICA
308
PROGRAMACIÓN DINÁMICA
Una vez vista la imagen donde las flechas indican la transición entre
los elementos que conforman la solución óptima podemos entender mejor la
solución del problema.
Vamos a representar la colección de elementos como nodos de un grafo
donde entre los elementos ai y aj a de existir una arista siempre y cuando se
cumpla las siguientes restricciones ai < aj y i < j. Si se analiza el grafo que
se construye es un grafo dirigido acı́clico (DAG). Por lo que la solución del
problema inicial radica en encontrar el camino mas largo el grafo construido.
for j=1,2,...,n:
L(j)=1+max{L(i):(i,j) ∈ E }
return maxj L(j)
Donde L(j) es la longitud del camino más largo que finaliza en el nodo
j. Este caso se le suma uno porque la distancia del camino no la define la
cantidad de aristas sino la cantidad de nodos que lo componen. La siguiente
implementación resuelve el problema planteado devolviendo los elementos
que conforman la LIS siguiendo la idea planteada anteriormente.
309
PROGRAMACIÓN DINÁMICA
v e c t o r <int> X ( n , 1 ) ;
v e c t o r <int> Y ( n , − 1 ) ;
f o r ( int i =1; i <n;++ i )
{
f o r ( int j =0; j <i ;++ j )
{
i f ( a [ j ] <a [ i ] )
{
i f (X[ i ]<X[ j ]+1)
{
X[ i ]=X[ j ] + 1 ;
Y[ i ]= j ;
}
}
}
}
v e c t o r <int> b ;
int k = 0 ;
f o r ( int i = 0 ; i <n ; ++ i )
i f (X[ k]<X[ i ] )
k=i ;
f o r ( int i = k ; i >=0; i=Y[ i ] )
b . push back ( a [ i ] ) ;
r e v e r s e ( b . b e g i n ( ) , b . end ( ) ) ;
return b ;
}
310
PROGRAMACIÓN DINÁMICA
311
PROGRAMACIÓN DINÁMICA
subsequence
subsequence
subsequence
subsequence
subsequence
Una idea para resolver este problema es utilizar la recursividad. Si compa-
ramos los últimos carácteres de la cadena donde vamos a buscar( a partir de
ahora vamos a nombrarla X) y del patrón que queremos encontrar (a partir
de ahora la nombraremos Y) siendo m y n las longitudes de las cadenas X y
Y respectivamente podemos encontrar dos posibilidades.
312
PROGRAMACIÓN DINÁMICA
return 0 ;
// S i e l u l t i m o c a r a c t e r de l a cadena c o i n c i d e con e l u l t i m o
// c a r a c t e r d e l patron ,
// 1 . Llamo de nuevo a l a f u n c i o n e x c l u y e n d o e l u l t i m o
caracter
// de l a cadena y d e l p a t r o n i n both s t r i n g and p a t t e r n
// 2 . Llamo de nuevo a l a f u n c i o n e x l u y e n d o s o l a m e n t e a l
ultimo
// c a r a c t e r de l a cadena
// s i n o son son i g u a l e s l o s u l t i m o c a r a c t e r e s l l a m o a l a
funcion excluyendo
// s o l a m e n t e e l u l t i m o c a r a c t e r de l a cadena ,
return ( (X[m−1] == Y[ n −1]) ? count (X, Y, m − 1 , n − 1 ) : 0 ) +
count (X, Y, m − 1 , n ) ;
}
int main ( )
{
s t r i n g X = " subsequence " ;
s t r i n g Y = "sue" ;
// cadena
// p a t r o n
c o u t << count (X, Y, X. s i z e ( ) , Y. s i z e ( ) ) ;
return 0 ;
}
313
PROGRAMACIÓN DINÁMICA
int main ( )
{
s t r i n g X = " subsequence " ;
s t r i n g Y = "sue" ;
// cadena
// p a t r o n
c o u t << count (X, Y, X. s i z e ( ) , Y. s i z e ( ) ) ;
return 0 ;
}
11.10. Needleman-Wunsch
El algoritmo de Needleman-Wunsch sirve para realizar alineamientos glo-
bales de dos secuencias. Se suele utilizar en el ámbito de la bioinformática
para alinear secuencias de proteı́nas o de ácidos nucleicos. Fue propuesto por
314
PROGRAMACIÓN DINÁMICA
315
PROGRAMACIÓN DINÁMICA
using namespace s t d ;
typedef long long l l d ;
typedef unsigned long long l l u ;
316
PROGRAMACIÓN DINÁMICA
int n , m;
int m a t c h s c o r e , m i s m a t c h s c o r e , g a p s c o r e ;
s t r i n g A, B ;
int dp [MAX N ] [ MAX N ] ;
i n l i n e p a i r <s t r i n g , s t r i n g > g e t o p t i m a l a l i g n m e n t ( )
{
s t r i n g retA , retB ;
s t a c k <char> SA , SB ;
int i i = n , j j = m;
while ( i i != 0 | | j j != 0 )
{
i f ( i i == 0 )
{
SA . push ( ’-’ ) ;
SB . push (B [ j j −1]) ;
j j −−;
}
e l s e i f ( j j == 0 )
{
SA . push (A[ i i −1]) ;
SB . push ( ’-’ ) ;
i i −−;
}
else
{
int S = (A[ i i −1] == B [ j j −1]) ? m a t c h s c o r e : −
mismatch score ;
i f ( dp [ i i ] [ j j ] == dp [ i i − 1 ] [ j j −1] + S )
{
SA . push (A[ i i −1]) ;
317
PROGRAMACIÓN DINÁMICA
SB . push (B [ j j −1]) ;
i i −−; j j −−;
}
e l s e i f ( dp [ i i − 1 ] [ j j ] > dp [ i i ] [ j j −1])
{
SA . push (A[ i i −1]) ;
SB . push ( ’-’ ) ;
i i −−;
}
else
{
SA . push ( ’-’ ) ;
SB . push (B [ j j −1]) ;
j j −−;
}
}
}
while ( ! SA . empty ( ) )
{
retA += SA . top ( ) ;
retB += SB . top ( ) ;
SA . pop ( ) ;
SB . pop ( ) ;
}
return m a k e p a i r ( retA , retB ) ;
}
int main ( )
{
n = 5 , m = 6;
match score = 2 , mismatch score = 1 , gap score = 1;
A = " CATGT " ;
B = " ACGCTG " ;
p r i n t f ( " %d\n" , needleman wunsch ( ) ) ;
p a i r <s t r i n g , s t r i n g > a l i g n m e n t = g e t o p t i m a l a l i g n m e n t ( ) ;
p r i n t f ( " %s\n %s\n" , a l i g n m e n t . f i r s t . c s t r ( ) , a l i g n m e n t . s e c o n d .
c str () ) ;
return 0 ;
}
318
PROGRAMACIÓN DINÁMICA
319
PROGRAMACIÓN DINÁMICA
320
PROGRAMACIÓN DINÁMICA
int m = a [ 0 ] . s i z e ( ) ;
int ans = 0 ;
v e c t o r <int> d (m, −1) , d1 (m) , d2 (m) ;
s t a c k <int> s t ;
f o r ( int i =0; i <n;++ i ) {
f o r ( int j =0; j <m;++ j ) {
i f ( a [ i ] [ j ]==1)
d [ j ]= i ;
}
while ( ! s t . empty ( ) )
s t . pop ( ) ;
while ( ! s t . empty ( ) )
s t . pop ( ) ;
321
PROGRAMACIÓN DINÁMICA
todos los números son positivos y otros caso cuando existen números positi-
vos y negativos.
322
PROGRAMACIÓN DINÁMICA
3330 - The Number Of The Witch Una vez leı́do el problema podemos
percatarnos que se nos esta pidiendo determinar cuantas subsecuencias con
los caracteres no necesariamente consecutivos existen dentro de una cadena
323
PROGRAMACIÓN DINÁMICA
.....
f o r ( int i = 1 ; i <= m; i ++)
f o r ( int j = 1 ; j <= n ; j ++)
{
i f ( t e x t [ i −1] == p a t t e r n [ j −1])
table [ i ] [ j ] = ( t a b l e [ i − 1 ] [ j −1]+ t a b l e [ i − 1 ] [ j ] ) %
MOD;
else
table [ i ] [ j ] = (0 + t a b l e [ i −1][ j ] ) %
MOD;
}
....
4004 - Nerdson and Alejandra Una vez leı́do el problema podemos per-
catarnos que se nos esta pidiendo determinar cuantas subsecuencias con los
caracteres no necesariamente consecutivos existen dentro de una cadena ta-
les que sean iguales a un determinado a un patrón que este caso es siempre
el mismo papa. Es evidente que aplicando el algoritmo visto en la sección
Contar la cantidad de ocurrencia de un patrón dentro de una ca-
dena como subsecuencia de caracteres no consecutivos llegamos a la
solución del ejercicio.
324
PROGRAMACIÓN DINÁMICA
....
DMOJ - Prince Una vez leı́do el problema nos podemos percatar que es
un clásico problema de mochila01 donde solo debemos aplicar el algoritmo
para cada caso y devolver la suma de todas mochilas juntas.
325
PROGRAMACIÓN DINÁMICA
1103 Coin Change Ejercicio clásico para resolver con el algoritmo Coin
Change donde para varios valores debes hallar la cantidad de fora que se pue-
den devolver utilizando billetes de las cuales se conocen sus denominaciones
2616 Easy Change Ejercicio clásico para resolver con el algoritmo Coin
Change para la variante en que se debe calcular un valor N la cantidad mı́ni-
ma de billetes que se debe devolver utilizando billetes cuyas denominaciones
son 1,3,5,6.
326
Capı́tulo 12
Teorı́a de juego
12.1. Lo básico
Un ejemplo simple es el siguiente juego, jugado por dos jugadores que se
turnan para moverse. Al principio hay n monedas. Cuando es el turno de un
jugador, él puede quitar 1, 3 o 4 monedas. El jugador que se lleva el último
es declarado ganador (en otras palabras, el jugador que no puede hacer un
327
TEORÍA DE JUEGO
b o o l e a n isWinning ( p o s i t i o n pos ) {
moves [ ] =// P o s i b l e s p o s i c i o n e s a l a s que puedo moverme d e s d e
l a p o s i c i o n pos ;
f o r ( a l l x i n moves )
i f ( ! isWinning ( x ) )
return true ;
return f a l s e ;
}
n 0 1 2 3 4 5 6 7 8 9 10 11
posición L W L W W W W L W L W W
Este juego podrı́a jugarse también con una regla (generalmente llamada
la regla de juego misere) de que el jugador que quita la última moneda es de-
clarado perdedor. Solo necesita cambiar el comportamiento de las posiciones
de terminal en el algoritmo WL. La tabla cambiará a esto:
n 0 1 2 3 4 5 6 7 8 9 10 11
posición W L W L W W W W L W L W
328
TEORÍA DE JUEGO
Se puede ver que si una posición está ganando o perdiendo depende solo
de las últimas k posiciones, donde k es el número máximo de monedas que
podemos quitar. Si bien solo hay 2k valores posibles para las secuencias de
la longitud k, nuestra secuencia se volverá periódica.
329
TEORÍA DE JUEGO
¿Por qué funciona? Desde las posiciones perdedoras solo podemos pasar
a las ganadoras: Si xor de los tamaños de las pilas es 0, se cambiará después
de nuestro movimiento (al menos 1 se cambiará a 0, por lo que en esa columna
habrá un número impar de 1s).
Desde las posiciones ganadoras es posible pasar a al menos una perdedora:
Si xor de los tamaños de las pilas no es 0, podemos cambiarlo a 0 encontrando
la columna más a la izquierda donde el número de 1s es impar, cambiando
uno de ellos a 0 y luego cambiando 0s o 1s en el lado derecho de para ganar
un número par de 1s en cada columna.
330
TEORÍA DE JUEGO
331
TEORÍA DE JUEGO
12.4. Conclusión
No se preocupe si ve un problema de teorı́a de juego durante un concurso:
puede ser similar a uno de los juegos descritos anteriormente o puede reducirse
a uno de ellos. Si no, solo piénselo en ejemplos concretos. Una vez que lo
descubres, la parte de codificación suele ser muy simple y directa. Buena
suerte y diviertete.
332
TEORÍA DE JUEGO
3608 - Chocolate Game Para resolver este problema nos podemos per-
catar que para una barra de chocolate de dimensiones AxB el resultado es
el mismo que BxA. Bien si simulamos para pequeños casos el juegos nos
podemos percatar que para barra de chocolates cuya dimensiones ancho y
largo sean iguales el jugador ganador es siempre es 2 en caso que lo anterior
no se cumpla el jugador ganador es 1.
333
TEORÍA DE JUEGO
334
Capı́tulo 13
Estructura de datos
Se separan dos capas de código bien diferentes, por una parte el algo-
ritmo que escribe el programador, y por otro las rutinas de acceso a las
diferentes estructuras.
335
ESTRUCTURA DE DATOS
13.1. Arreglos
En programación, un arreglo (llamados en inglés array) es una zona de
almacenamiento continuo, que contiene una serie de elementos del mismo
tipo.
Esta estructura de dato son adecuadas para situaciones en las que el ac-
ceso a los datos se realice de forma aleatoria e impredecible. Por el contrario,
si los elementos pueden estar ordenados y se va a utilizar acceso secuencial
serı́a más adecuado utilizar una lista, ya que esta estructura puede cambiar
de tamaño fácilmente durante la ejecución de un programa.
Todo arreglo se compone de un determinado número de elementos. Cada
elemento es referenciado por la posición que ocupa dentro del vector. Dichas
posiciones son llamadas ı́ndice y siempre son correlativos. Existen tres formas
de indexar los elementos de un arreglo:
336
ESTRUCTURA DE DATOS
13.1.1. C++
En lenguaje de programación C++ el arreglo se puede declarar según la
situación. Ahora veremos cada una de ellas
/∗ Sabemos l a c a n t i d a d de e l e m e n t o s a p r i o r i ,
<t i p o d e da to > <n o m b r e a r r e g l o > [< c a n t i d a d > ] ; ∗/
bool i s P i m e s [ 1 0 0 ] ;
/∗ Para a c c e d e r o m o d i f i c a r a l g u n e l e m e n t o d e l a r r e g l o l o
hacemos de
de l a s i g u i e n t e manera ∗/
int primeraNota=n o t a s [ 0 ] ;
n o t a s [ 2 3 ] = cantidadMaxima ;
n o t a s [ cantidadMaxima −1]=cantidadMaxima ;
int ultimaNota=n o t a s [ cantidadMaxima − 1 ] ;
13.1.2. Java
Los arreglos de Java se tratan como objetos de una clase predefinida.
Los arrays son objetos, pero con algunas caracterı́sticas propias. Los arreglos
pueden ser asignados a objetos de la clase Object y los métodos de Object
pueden ser utilizados con arreglos.
/∗ D e c l a r a c i o n de un a r r e g l o . Se i n i c i a l i z a a n u l l ∗/
int [ ] v e c t o r ;
/∗ a r r e g l o de 10 e n t e r o s , i n i c i a l i z a d o s a 0 ∗/
v e c t o r = new int [ 1 0 ] ;
/∗ D e c l a r a c i o n e i n i c i a l i z a c i o n de un a r r e g l o de 3 e l e m e n t o s
337
ESTRUCTURA DE DATOS
con l o s v a l o r e s e n t r e l l a v e s ∗/
double [ ] v = { 1 . 0 , 2 . 6 5 , 3 . 1 } ;
/∗ Se c r e a un a r r e g l o de 5 r e f e r e n c i a s a o b j e t o s
Las 5 r e f e r e n c i a s son i n i c i a l i z a d a s a n u l l ∗/
MyClass [ ] l i s t a =new MyClass [ 5 ] ;
/∗
Se a s i g n a a l i s t a [ 1 ] l a r e f e r e n c i a a l nuevo o b j e t o
El r e s t o ( l i s t a [ 2 ] . . . l i s t a [ 4 ] s i g u e n con v a l o r n u l l
∗/
l i s t a [ 1 ] = new MyClass ( ) ;
13.2. Matrices
Una matriz es un arreglo de areglos fila, o más en concreto un arreglo de
referencias a los arreglos fila. Con este esquema, cada fila podrı́a tener un
número de elementos diferente.
13.2.1. C++
Los arreglos bidimensionales o matrices en C++ se pude declarar similar
a como se hace un arreglo unidimensional.
/∗ Se c o n o c e de antemano l a s d i m e n s i o n e s e s t a manera e s
e s t a t i c a s e recomienda que s e a dinamica ∗/
int mat [ 3 ] [ 4 ] ;
/∗Con l o s v a l o r e s c o n o c i d o s ∗/
double c a r r o t s [ 3 ] [ 4 ] { { 2 . 5 , 3 . 2 , 3 . 7 , 4 . 1 } , // p r i m e r a f i l a
{ 4 . 1 , 3 . 9 , 1 . 6 , 3 . 5 } , // segunda f i l a
{ 2 . 8 , 2 . 3 , 0 . 9 , 1 . 1 } // t e r c e r a f i l a
};
338
ESTRUCTURA DE DATOS
13.2.2. Java
Los arrays bidimensionales de Java se crean de un modo muy similar al
de C++ (con reserva dinámica de memoria). En Java una matriz se puede
crear directamente en la forma,
int [ ] [ ] mat ;
// c r e a r una m a t r i z 3 x3
// s e i n i c i a l i z a n a c e r o
double mat [ ] [ ] = new double [ 3 ] [ 3 ] ;
int [ ] [ ] b = { { 1 , 2 , 3 } ,
{ 4 , 5 , 6 } , // e s t a coma e s p e r m i t i d a
};
int c = new [ 3 ] [ ] ; // s e c r e a e l a r r a y de r e f e r e n c i a s a a r r a y s
c [ 0 ] = new int [ 5 ] ;
c [ 1 ] = new int [ 4 ] ;
c [ 2 ] = new int [ 8 ] ;
13.3. Lista
Las listas constituyen una de las estructuras lineales más flexibles, por-
que pueden crecer y acortarse según se requiera, insertando o suprimiendo
339
ESTRUCTURA DE DATOS
13.3.1. C++
En el caso de C++ propone tres variantes de estructura de datos de
tipo dato lista las cuales varı́an entre sı́ por su modelo de implementación.
A continuación veremos cada una de las variantes. De forma general cada
estructura tienen definidas las operaciones para manipular y gestionar los
datos almacenados.
340
ESTRUCTURA DE DATOS
Vector
#i n c l u d e <v e c t o r >
Para declarar un vector solo basta con poner algo como lo que sigue:
v e c t o r <T> n o m b r e d e l v e c t o r ;
Donde T debe ser uno de los tipos de datos definidos por el lenguaje de
progrmación o por el propio programador. El vector es muy útil cuando se va
acceder a los elementos conocidos su posición dentro de la estructura y se va
añadir o eliminar elementos de última posición. No presenta igual desempeño
cuando las inserciones y eliminaciones se producen en otras posiciones. Una
forma muy eficiente de utilizar el vector es una vez creado inicializarlo con
la cantidad máxima de elementos que puede alamcenar siempre y cuando se
conozca este valor.
int main ( )
{
unsigned int i ;
// v a r i a n t e s para c o n s t u i r un v e c t o r :
// un v e c t o r de e n t e r o s v a c i o s
v e c t o r <int> f i r s t ;
/∗ c r e a n d o un v e c t o r i t e r a n d o s o b r e o t r o ∗/
v e c t o r <int> t h i r d ( s e c o n d . b e g i n ( ) , s e c o n d . end ( ) ) ;
/∗ c r e a n d o un v e c t o r c o p i a d e l t e r c e r v e c t o r ∗/
v e c t o r <int> f o u r t h ( t h i r d ) ;
341
ESTRUCTURA DE DATOS
/∗ c r e a n d o un v e c t o r a p a r t i r de un v e c t o r ∗/
int myints [ ] = { 1 6 , 2 , 7 7 , 2 9 } ;
v e c t o r <int> f i f t h ( myints , myints + s i z e o f ( myints ) / s i z e o f (
int ) ) ;
/∗ a d i c i o n a n d o un e l e m e n t o a l v e c t o r ∗/
f i r s t . push back ( 2 ) ;
/∗ l i m p i a r un v e c t o r ∗/
second . c l e a r ( ) ;
/∗ s a b e r s i un v e c t o r no t i e n e e l e m e n t o ∗/
i f ( f o u r t h . empty ( ) )
cout<<" Vector empty "<<e n d l ;
else
cout<<" Vector not empty "<<e n d l ;
c o u t << e n d l ;
return 0 ;
}
Deque
Deque (generalmente pronunciado como deck) es un acrónimo irregular de
doble cola. Las colas de doble final son una clase de contenedores de secuencia.
Como tales, sus elementos están ordenados siguiendo una secuencia lineal
estricta.
Las bibliotecas especı́ficas pueden implementar Deques de diferentes ma-
neras, pero en todos los casos permiten que se pueda acceder a los elementos
individuales a través de iteradores de acceso aleatorio, con el almacenamiento
siempre manejado automáticamente (expandiéndose y contrayéndose según
sea necesario).
Las secuencias de Deque tienen las siguientes propiedades:
342
ESTRUCTURA DE DATOS
int main ( )
{
unsigned int i ;
// v a r i a n t e s para c o n s t u i r un deque :
// un deque de e n t e r o s v a c i o s
deque<int> f i r s t ;
/∗ c r e a n d o un deque i t e r a n d o s o b r e o t r o ∗/
deque<int> t h i r d ( s e c o n d . b e g i n ( ) , s e c o n d . end ( ) ) ;
/∗ c r e a n d o un deque c o p i a d e l t e r c e r v e c t o r ∗/
deque<int> f o u r t h ( t h i r d ) ;
/∗ c r e a n d o un deque a p a r t i r de un a r r e g l o ∗/
343
ESTRUCTURA DE DATOS
int myints [ ] = { 1 6 , 2 , 7 7 , 2 9 } ;
deque<int> f i f t h ( myints , myints + s i z e o f ( myints ) / s i z e o f ( int )
);
/∗ a d i c i o n a n d o un e l e m e n t o a l deque ∗/
f i r s t . push back ( 2 ) ;
/∗ l i m p i a r un deque ∗/
second . c l e a r ( ) ;
/∗ s a b e r s i un deque no t i e n e e l e m e n t o ∗/
i f ( f o u r t h . empty ( ) )
cout<<" deque empty "<<e n d l ;
else
cout<<" deque not empty "<<e n d l ;
c o u t << e n d l ;
return 0 ;
}
List
Las listas son una clase de contenedores de secuencia. Como tales, sus
elementos están ordenados siguiendo una secuencia lineal.
Los contenedores de listas se implementan como listas doblemente enla-
zadas; Las listas doblemente vinculadas pueden almacenar cada uno de los
elementos que contienen en ubicaciones de almacenamiento diferentes y no
relacionadas. El orden se mantiene por la asociación a cada elemento de un
enlace al elemento que lo precede y un enlace al elemento que lo sigue.
Esto proporciona las siguientes ventajas para listar contenedores:
344
ESTRUCTURA DE DATOS
int main ( )
{
l i s t <int> f i r s t ;
l i s t <int> s e c o n d ( 4 , 1 0 0 ) ;
l i s t <int> t h i r d ( s e c o n d . b e g i n ( ) , s e c o n d . end ( ) ) ;
l i s t <int> f o u r t h ( t h i r d ) ;
int myints [ ] = { 1 6 , 2 , 7 7 , 2 9 } ;
l i s t <int> f i f t h ( myints , myints+s i z e o f ( myints ) / s i z e o f ( int ) ) ;
c o u t << e n d l ;
return 0 ;
}
13.3.2. Java
La interface List define métodos para operar con colecciones ordenadas y
que pueden tener elementos repetidos. Por ello, dicha interface declara méto-
dos adicionales que tienen que ver con el orden y con el acceso a elementos
o rangos de elementos. Además de los métodos de Collection, la interface
List declara los métodos siguientes:
345
ESTRUCTURA DE DATOS
public i n t e r f a c e j a v a . u t i l . L i s t extends j a v a . u t i l . C o l l e c t i o n
{
public abstract void add ( j a v a . l a n g . Object ) ;
public abstract void add ( int , j a v a . l a n g . Object ) ;
public abstract boolean addAll ( int , j a v a . u t i l . C o l l e c t i o n ) ;
public abstract boolean addAll ( j a v a . u t i l . C o l l e c t i o n ) ;
public abstract j a v a . l a n g . Object g e t ( int ) ;
public abstract int indexOf ( j a v a . l a n g . Object ) ;
public abstract int l a s t I n d e x O f ( j a v a . l a n g . Object ) ;
public abstract j a v a . u t i l . L i s t I t e r a t o r l i s t I t e r a t o r ( ) ;
public abstract j a v a . u t i l . L i s t I t e r a t o r l i s t I t e r a t o r ( int ) ;
public abstract j a v a . l a n g . Object remove ( int ) ;
public abstract j a v a . l a n g . Object s e t ( int , j a v a . l a n g . Object ) ;
public abstract j a v a . u t i l . L i s t s u b L i s t ( int , int ) ;
}
346
ESTRUCTURA DE DATOS
import j a v a . u t i l . L i s t ;
import j a v a . u t i l . A r r a y L i s t ;
import j a v a . u t i l . L i n k e d L i s t ;
Declararlas.
l 1 . Add ( 1 0 1 ) ;
13.4. Pila
Básicamente es una lista en la cual todas las operaciones de inserción y
borrado se producen en uno de los extremos de la lista. Un ejemplo gráfico
es una pila de libros en un cajón. A medida que vamos recibiendo más libros
los ubicamos en la parte superior. En todo momento tenemos acceso sólo
al libro que se encuentra sobre el “tope”de la pila. Si queremos acceder a
algún libro que se encuentra más abajo (digamos en la quinta posición desde
el tope) debemos sacar los primeros cuatro libros y ponerlos en algún lugar
para poder acceder al mismo. La Pila es el tı́pico ejemplo de la estructura tipo
“LIFO”(por “Last In First Out”, es decir “el último en entrar es el primero
en salir”).
Una pila (stack en inglés) es una lista ordenada o estructura de datos que
permite almacenar y recuperar datos. Esta estructura se aplica en multitud
de ocasiones en el área de informática debido a su simplicidad y ordenación
implı́cita de la propia estructura. Para el manejo de los datos se cuenta con
dos operaciones básicas: apilar (push), que coloca un objeto en la pila, y su
operación inversa, retirar (o desapilar, pop), que retira el último elemento
apilado.
En cada momento sólo se tiene acceso a la parte superior de la pila, es
decir, al último objeto apilado (denominado TOS, Top of Stack en inglés).
La operación retirar permite la obtención de este elemento, que es retirado
de la pila permitiendo el acceso al siguiente (apilado con anterioridad), que
pasa a ser el nuevo TOS.
347
ESTRUCTURA DE DATOS
Implementación de recursividad.
13.4.1. C++
13.4.2. Java
En java para se uso de la pila debemos hacer uso de la clase Stack la
cual extiende de la clase Vector. Para incluirla en nuestra solución debemos
primero importarla del paquete java en el subpaquete util
import j a v a . u t i l . Stack ;
Luego solo debemos crear una instancia de esta clase y tenemos una pila
lista para ser usada en nuestra solución.
S t r i n g t o p e=p i l a . pop ( ) ;
348
ESTRUCTURA DE DATOS
S t r i n g t o p e=p i l a . peek ( ) ;
13.5. Cola
Por contraposición con la pila, la cola es un contenedor de tipo “FI-
FO”(por “First In First Out”, el primero en entrar es el primero en salir). El
ejemplo clásico es la cola de la caja en el supermercado. La cola es un objeto
muchas veces usado como buffer o pulmón, es decir un contenedor donde
almacenar una serie de objetos que deben ser procesados, manteniendo el
orden en el que ingresaron. La cola es también, como la pila, un subtipo de
la lista llama también a ser implementado como un adaptador.
Bicolas: son colas en donde los nodos se pueden añadir y quitar por
ambos extremos; se les llama DEQUE (Double Ended QUEue). Para
349
ESTRUCTURA DE DATOS
representar las bicolas lo podemos hacer con un array circular con Inicio
y Fin que apunten a cada uno de los extremos. Hay variantes:
13.5.1. C++
13.5.2. Java
350
ESTRUCTURA DE DATOS
13.6.1. C++
13.6.2. Java
Soporte
La Librerı́a Estándar de Plantillas de C++ proporciona las clases genéri-
cas std::deque y std::list, para las implemteaciones con vector dinámico y
351
ESTRUCTURA DE DATOS
Complejidad
En una implementación realizada con una lista doblemente enlazada la
complejidad de todas las iteraciones es O(1), excepto para acceder a un ele-
mento que no se encuentre en uno de los extremos de la estructura, que la
complejidad será O(n).
Si se implementa mediante un vector, la complejidad de las operaciones
de la cola doblemente terminada coincide con la de la implementación con
una lista.
13.7.1. C++
13.7.2. Java
13.8. Conjunto
Un conjunto es una colección de “miembros”o “elementos”de un “conjun-
to universal”. Por contraposición con las listas y otros contenedores vistos
previamente, todos los miembros de un conjunto deben ser diferentes, es decir
no puede haber dos copias del mismo elemento. Si bien para definir el concep-
to de conjunto sólo es necesario el concepto de igualdad o desigualdad entre
los elementos del conjunto universal, en general las representaciones de con-
juntos asumen que entre ellos existe además una “relación de orden estricta”,
que usualmente se denota como <. A veces un tal orden no existe en forma
natural y es necesario saber definirlo, aunque sea sólo para implementar el
tipo conjunto.
13.8.1. C++
13.8.2. Java
13.9. Diccionario
Los diccionarios, también llamados matrices asociativas, deben su nombre
a que son colecciones que relacionan una clave y un valor.El primer valor se
352
ESTRUCTURA DE DATOS
13.9.1. C++
La estructura de datos map en C++, se define como un contenedor aso-
ciativo ordenado que consta de pares de valores key-valor; donde key en una
clave única que define a cada valor. En orden de poder crear objetos map en
un programa se debe incluir el uso de la clase map mediante la directiva:
#include <map>
pair es una estructura independiente y puede ser usada con diversos fines,
sin embargo, la importancia de pair radica en el hecho de que esta es usada
como estructura elemental para construir contenedores tipo map.
Un objeto de tipo map se puede definir mediante los siguientes construc-
tores:
#include <map>
map ( ) ;
map( const map& m ) ;
map( i t e r a t o r s t a r t , i t e r a t o r end ) ;
map( i t e r a t o r s t a r t , i t e r a t o r end , const key compare& cmp ) ;
map( const key compare& cmp ) ;
353
ESTRUCTURA DE DATOS
Un objeto map puede ser comparado y/o asignado con los operadores
estándar de C++: ==, ! =, <=, >=, <, > y =. Además, si se desea acceder
a un único elemento dentro de la estructura se puede utilizar el operador [ ].
Téngase en cuenta que dos objetos de tipo map son iguales si:
using namespace s t d ;
int main ( )
{
c o u t << e n d l << "Otra simple prueba de map" << e n d l ;
map<s t r i n g , s t r i n g > d i r e c t o r i o ;
354
ESTRUCTURA DE DATOS
map<s t r i n g , s t r i n g > : : i t e r a t o r p = d i r e c t o r i o . f i n d ( s ) ;
i f ( p != d i r e c t o r i o . end ( ) )
c o u t << " Numero telefonico de : " << s << " = " << p−>
s e c o n d << e n d l ;
else
c o u t << s << " no esta en el directorio .\n" ;
return 0 ;
}
El segundo
using namespace s t d ;
int main ( )
{
c o u t << "\nUna simple prueba de map\n" ;
c o u t << " ........................\ n" ;
map<s t r i n g , double> semana ;
355
ESTRUCTURA DE DATOS
return 0 ;
}
13.9.2. Java
13.10. Estructura
En programación, una estructura de datos es una forma particular de
organizar datos en una computadora para que pueda ser utilizado de manera
eficiente.
13.11. Árbol
Los árboles son contenedores que permiten organizar un conjunto de ob-
jetos en forma jerárquica. Ejemplos tı́picos son los diagramas de organización
de las empresas o instituciones y la estructura de un sistema de archivos en
una computadora. Los árboles sirven para representar fórmulas, la descom-
posición de grandes sistemas en sistemas más pequeños en forma recursiva y
aparecen en forma sistemática en muchı́simas aplicaciones de la computación
cientı́fica. Una de las propiedades más llamativas de los árboles es la capa-
cidad de acceder a muchı́simos objetos desde un punto de partida o raı́z en
unos pocos pasos.
356
ESTRUCTURA DE DATOS
árboles binarios son los árboles binarios de búsqueda, los montı́culos binarios
y Codificación de Huffman.
En teorı́a de grafos, se usa la siguiente definición: ((Un árbol binario es
un grafo conexo, acı́clico y no dirigido tal que el grado de cada vértice no es
mayor a 3)). De esta forma solo existe un camino entre un par de nodos.
Un árbol binario con enraizado es como un grafo que tiene uno de sus
vértices, llamado raı́z, de grado no mayor a 2. Con la raı́z escogida, cada
vértice tendrá un único padre, y nunca más de dos hijos. Si rehusamos el
requerimiento de la conectividad, permitiendo múltiples componentes conec-
tados en el grafo, llamaremos a esta última estructura un bosque’.
357
ESTRUCTURA DE DATOS
#include <complex>
#define MAX N 1000001
using namespace s t d ;
typedef long long l l d ;
int numComponents , m;
struct Node
{
int p a r e n t ;
int rank ;
};
Node DSU[MAX N ] ;
358
ESTRUCTURA DE DATOS
int main ( )
{
numComponents = 4 , m = 8 ;
MakeSet ( 1 ) ;
MakeSet ( 2 ) ;
MakeSet ( 3 ) ;
MakeSet ( 4 ) ;
Union ( 1 , 2) ;
Union ( 2 , 1) ;
Union ( 2 , 3) ;
Union ( 1 , 3) ;
return 0 ;
}
359
ESTRUCTURA DE DATOS
f u n c t i o n Consulta ( ind )
suma := 0
while i n d > 0
suma := suma + t r e e [ i n d ]
i n d := i n d − ( i n d & −i n d )
return suma
f u n c t i o n M o d i f i c a r ( ind , v a l )
while i n d < MaxVal
t r e e [ i n d ] : = t r e e [ i n d ]+ v a l
i n d := i n d + ( i n d & −i n d )
360
ESTRUCTURA DE DATOS
int n ;
int b i t [MAX N ] ;
i n l i n e int r e a d ( int x )
{
int r e t = 0 ;
while ( x > 0 )
{
r e t += b i t [ x ] ;
x −= ( x & −x ) ;
}
return r e t ;
}
int main ( )
{
n = 10;
update ( 1 , 1 ) ;
update ( 3 , 1 ) ;
update ( 5 , 5 ) ;
update ( 2 , −2) ;
update ( 5 , −1) ;
p r i n t f ( " %d\n" , r e a d ( 6 ) ) ;
return 0 ;
}
Tanto la operación read como update tiene complejidad O(log N).La se-
gunda variante es una plantilla de la estructura que puede determinar la
suma dado un rango, ası́ como la actualización del valor dado un ı́ndice.
361
ESTRUCTURA DE DATOS
362
ESTRUCTURA DE DATOS
363
ESTRUCTURA DE DATOS
Si no:
Si v no es hoja
#i n c l u d e <c s t d i o >
#i n c l u d e <v e c t o r >
// Tipo de l o s puntos
#d e f i n e Po in t f l o a t
364
ESTRUCTURA DE DATOS
// R e p r e s e n t a +i n f i n i t o
#d e f i n e FLT MAX 1E+37
using namespace s t d ;
struct Segment
{
Po i nt minValue ;
Po i nt maxValue ;
};
// Nodos d e l a r b o l de segmento
struct BinaryTree
{
Po i nt minValue ;
bool minOpen ;
Po i nt maxValue ;
bool maxOpen ;
v e c t o r <Segment> s u b S e t ;
BinaryTree ∗ l e f t C h i l d ;
BinaryTree ∗ r i g h t C h i l d ;
};
// B u i l t methods
365
ESTRUCTURA DE DATOS
BinaryTree ∗ B u i l t B a l a n c e d B i n a r y T r e e ( P oi nt e n d P o i n t s [ ] , int
countEndPoint )
{
BinaryTree ∗ e l e m e n t a r y I n t e r v a l [ countEndPoint ∗ 2 + 1 ] ;
P oi nt minValue = −FLT MAX;
f o r ( int i = 0 ; i < countEndPoint ; i ++)
{
int towI = 2 ∗ i ;
e l e m e n t a r y I n t e r v a l [ towI ] = CreateLeafNode ( minValue ,
e n d P o i n t s [ i ] , true , true ) ;
e l e m e n t a r y I n t e r v a l [ towI + 1 ] = CreateLeafNode (
endPoints [ i ] , endPoints [ i ] , false , false ) ;
minValue = e n d P o i n t s [ i ] ;
}
int countNodes = countEndPoint ∗ 2 + 1 ;
e l e m e n t a r y I n t e r v a l [ countNodes − 1 ] = CreateLeafNode (
e n d P o i n t s [ countEndPoint − 1 ] , FLT MAX, true , true ) ;
while ( countNodes > 1 )
{
f o r ( int i = 0 ; i < countNodes / 2 ; i ++)
{
int towI = 2 ∗ i ;
BinaryTree ∗newNode = Union ( e l e m e n t a r y I n t e r v a l [
towI ] , e l e m e n t a r y I n t e r v a l [ towI + 1 ] ) ;
newNode−>l e f t C h i l d = e l e m e n t a r y I n t e r v a l [ towI ] ;
newNode−>r i g h t C h i l d = e l e m e n t a r y I n t e r v a l [ towI +
1];
newNode−>s u b S e t = v e c t o r <Segment >() ;
e l e m e n t a r y I n t e r v a l [ i ] = newNode ;
}
i f ( countNodes % 2 )
{
e l e m e n t a r y I n t e r v a l [ countNodes / 2 ] =
e l e m e n t a r y I n t e r v a l [ countNodes − 1 ] ;
}
366
ESTRUCTURA DE DATOS
367
ESTRUCTURA DE DATOS
// Query methods
i n l i n e bool C o nt a i nP o i n t ( BinaryTree ∗ t r e e , Po in t p o i n t )
{
return ( t r e e −>minValue < p o i n t | ( t r e e −>minValue == p o i n t &
! t r e e −>minOpen ) )
& ( t r e e −>maxValue > p o i n t | ( t r e e −>maxValue == p o i n t &
! t r e e −>maxOpen ) ) ;
}
368
ESTRUCTURA DE DATOS
369
ESTRUCTURA DE DATOS
13.16.1. Operaciones
Construción Un árbol de rango 1-dimensional en un conjunto de n ele-
mentos es un árbol de búsqueda binario, que se puede construir en el tiempo
O ( n log n ). Los árboles de rango en dimensiones más altas se constru-
yen recursivamente construyendo un árbol de búsqueda binario equilibrado
en la primera coordenada de los elementos y luego, para cada vértice v en
este árbol, construyendo un árbol de distribución (d-1)-dimensional en los
elementos contenidos en el subárbol de v. Construir un árbol de rango de
esta manera requerirı́a tiempo O (nlog d n).
370
ESTRUCTURA DE DATOS
#i n c l u d e <s t d i o . h>
#i n c l u d e <math . h>
#i n c l u d e <s t r i n g . h>
#i n c l u d e <i o s t r e a m >
#i n c l u d e <v e c t o r >
#i n c l u d e < l i s t >
#i n c l u d e <s t r i n g >
#i n c l u d e <a l g o r i t h m >
#i n c l u d e <queue>
#i n c l u d e <s t a c k >
#i n c l u d e <s e t >
#i n c l u d e <map>
#i n c l u d e <complex>
#d e f i n e MID ( l e f t +r i g h t ) /2
#d e f i n e MAX N 1000001
#d e f i n e MAX TREE (MAX N << 2 )
#d e f i n e INF 987654321
using namespace s t d ;
371
ESTRUCTURA DE DATOS
int n ;
int n i z [MAX N ] ;
int RT[MAX TREE ] ;
int main ( )
{
n = 6;
niz [ 1 ] = 4;
niz [ 2 ] = 2;
niz [ 3 ] = 5;
niz [ 4 ] = 1;
niz [ 5 ] = 6;
372
ESTRUCTURA DE DATOS
niz [ 6 ] = 3;
InitTree (1 , 1 , n) ;
p r i n t f ( " %d\n" , Query ( 1 , 1 , 3 , 1 , n ) ) ;
Update ( 1 , 4 , 1 0 , 1 , n ) ;
Update ( 1 , 5 , 0 , 1 , n ) ;
p r i n t f ( " %d\n" , Query ( 1 , 4 , 6 , 1 , n ) ) ;
return 0 ;
}
373
ESTRUCTURA DE DATOS
374
ESTRUCTURA DE DATOS
struct s p l a y t r e e
{
struct node
{
375
ESTRUCTURA DE DATOS
int key ;
node ∗ l , ∗ r ;
node ( int key = 0 , node ∗ l = 0 , node ∗ r = 0 ) :
key ( key ) , l ( l ) , r ( r ) { }
} ∗ root ;
return t ;
}
node ∗ f i n d ( int x )
{
root = splay (x , root ) ;
i f ( x == r o o t −>key )
return r o o t ;
else
return 0 ;
}
376
ESTRUCTURA DE DATOS
void i n s e r t ( int x )
{
i f ( ! root )
r o o t = new node ( x ) ;
else
{
root = splay (x , root ) ;
i f ( x < r o o t −>key )
{
node ∗ t = new node ( x , r o o t −>l , r o o t ) ;
r o o t −>l = 0 ;
root = t ;
}
e l s e i f ( x > r o o t −>key )
{
node ∗ t = new node ( x , r o o t , r o o t −>r ) ;
r o o t −>r = 0 ;
root = t ;
}
}
}
void e r a s e ( int x )
{
if (! find (x) )
return ;
i f ( ! r o o t −>l )
r o o t = r o o t −>r ;
else
{
node ∗ t = r o o t −>r ;
r o o t = s p l a y ( x , r o o t −>l ) ;
r o o t −>r = t ;
}
}
377
ESTRUCTURA DE DATOS
378
ESTRUCTURA DE DATOS
379
ESTRUCTURA DE DATOS
380
ESTRUCTURA DE DATOS
Los dos bloques que contienen los lı́mites se pueden buscar ingenua-
mente. Los elementos fuera del lı́mite ni siquiera necesitan ser mirados.
Esto se puede hacer en tiempo logarı́tmico.
381
ESTRUCTURA DE DATOS
Para cada árbol, el posible resultado para todas las consultas debe al-
macenarse. Esto se reduce a entradas s2 u O (log2 n). Esto significa que el
tamaño general de la tabla es O(n).
Para buscar resultados de manera eficiente, el árbol cartesiano (fila) co-
rrespondiente a un bloque especı́fico debe ser direccionable en tiempo cons-
tante. La solución es almacenar los resultados para todos los árboles en una
matriz y encontrar una proyección única de árboles binarios a enteros pa-
ra direccionar las entradas. Esto se puede lograr haciendo una búsqueda de
amplitud primero a través del árbol y agregando nodos hoja para que cada
nodo existente en el árbol cartesiano tenga exactamente dos hijos. El núme-
ro entero se genera representando cada nodo interno como un bit 0 y cada
hoja como un bit en una palabra de bit (atravesando nuevamente el árbol
en orden de nivel). Esto lleva a un tamaño de registron
4
para cada árbol. Para
habilitar el acceso aleatorio en tiempo constante a cualquier árbol, los árboles
no contenidos en la matriz original también deben incluirse. Una matriz con
logn
ı́ndices de logn
4
bits de longitud tiene un tamaño 2 4 = 0(n)
Los RMQ se pueden usar para resolver el problema de ancestro común
más bajo y se usan como una herramienta para muchas tareas en la coinci-
dencia de cadenas exacta y aproximada.
A continuación la implementación de esta estructura de datos.
382
ESTRUCTURA DE DATOS
b [ i ]=min ( b [ i ] , b [ i+k ] ) ;
Por
b [ i ]=max( b [ i ] , b [ i+k ] ) ;
De igual modificamos
383
ESTRUCTURA DE DATOS
por
384
ESTRUCTURA DE DATOS
a[0], a[1], . . . , a[S − 1], a[S], . . . , a[2S − 1], . . . , a[(S − 1) · s], . . . , a[N ]
| {z } | {z } | {z }
b[0] b[1] b[S-1]
El último bloque puede tener menos elementos que los otros (si N no
es un múltiplo de S ), no es importante para la discusión (ya que se puede
manejar fácilmente). Por lo tanto, para cada bloque k, conocemos la suma
de elementos en él b[k]:
mı́n (n−1,(k+1)·s−1)
X
b[k] = a[i]
i=k·s
13.19.2. Implementación
Comencemos con la implementación más simple:
// d a t o s de e n t r a d a
int n ;
385
ESTRUCTURA DE DATOS
v e c t o r <int> a ( n ) ;
// p r e r p o c e s a m i e n t o
int l e n = ( int ) s q r t ( n +.0) +1; // l o n g i t u d y c a n t i d a d de b l o q u e s
v e c t o r <int> b ( l e n ) ;
f o r ( int i =0; i <n ; ++i )
b [ i / l e n ]+=a [ i ] ;
// r e s p o n d e r l a s c o n s u l t a s
for ( ; ; ) {
int l , r ;
// l e e r l o s d a t o s de e n t r a d a s para l a s i g u i e n t e c o n s u l t a
int sum = 0 ;
f o r ( int i=l ; i<=r ; )
i f ( i % l e n==0 && i+l e n −1 <= r ) {
// s i todo e l b l o q u e que comienza en i p e r t e n e c e a [ l ; r ]
sum += b [ i / l e n ] ;
i += l e n ;
} else {
sum += a [ i ] ;
++i ;
}
}
int sum=0;
int c l=l / l e n , c r=r / l e n ;
i f ( c l==c r )
f o r ( int i=l ; i<=r ; ++i )
sum += a [ i ] ;
else {
f o r ( int i=l , end=( c l +1)∗ l e n −1; i<=end ; ++i )
sum += a [ i ] ;
f o r ( int i=c l +1; i<=c r −1; ++i )
sum += b [ i ] ;
f o r ( int i=c r ∗ l e n ; i<=r ; ++i )
sum += a [ i ] ;
}
386
ESTRUCTURA DE DATOS
387
ESTRUCTURA DE DATOS
388
ESTRUCTURA DE DATOS
es posible cuando se nos permite responder las consultas en modo fuera del
orden establecido.
13.20.1. Implementación
En el algoritmo de Mo utilizamos dos funciones para agregar un ı́ndice y
para eliminar un ı́ndice del rango que actualmente mantenemos.
int b l o c k s i z e ;
struct Query {
int l , r , i d x ;
bool operator <(Query o t h e r ) const {
return m a k e p a i r ( l / b l o c k s i z e , r ) <
make pair ( other . l / b l o c k s i z e , other . r ) ;
}
};
v e c t o r <int> m o s a l g o r i t h m ( v e c t o r <Query> q u e r i e s ) {
v e c t o r <int> a ns w er s ( q u e r i e s . s i z e ( ) ) ;
s o r t ( q u e r i e s . b e g i n ( ) , q u e r i e s . end ( ) ) ;
// TODO: I n i c i a l i z a r l a e s t r u c t u r a
int c u r l = 0 ;
int c u r r = −1;
// i n v a r i a n t e : La e s t r u c t u r a de d a t o s s i e m p r e r e f l e j a e l
rango [ c u r l , c u r r ]
f o r ( Query q : q u e r i e s ) {
while ( c u r l > q . l ) {
c u r l −−;
add ( c u r l ) ;
}
while ( c u r r < q . r ) {
c u r r ++;
add ( c u r r ) ;
}
while ( c u r l < q . l ) {
remove ( c u r l ) ;
c u r l ++;
}
389
ESTRUCTURA DE DATOS
while ( c u r r > q . r ) {
remove ( c u r r ) ;
c u r r −−;
}
a ns w er s [ q . i d x ] = g e t a n s w e r ( ) ;
}
return a ns w er s ;
}
13.20.2. Complejidad
Ordenar todas las consultas tomará O(Q log Q).
¿Qué hay de las otras operaciones? ¿Cuántas veces se llamará agregar y
quitar?
Digamos que el tamaño del bloque es S.
Si miramos solo miramos todas las consultas que con el ı́ndice izquierdo
en el mismo bloque. Las consultas se ordenan por el ı́ndice derecho. Por lo
tanto, llamaremos a add (cur r) y remove (cur r) solo O O(N ) veces para
todas estas consultas combinadas. Esto proporciona llamadas O( NS N ) para
todos los bloques.
El valor de cur l puede cambiar como máximo O(S) durante entre dos
consultas. Por lo tanto, tenemos un adicional O(SQ) llamadas de add (cur l)
y remove (cur√l). √
Para S ≈ N esto da operaciones O((N + Q) N ) en total. Por lo tanto,
390
ESTRUCTURA DE DATOS
√
la complejidad es O((N + Q)F N ) donde O(F ) es la complejidad de las
funciones agregar y quitar.
√
El tamaño de bloque de √ N no siempre ofrece el mejor tiempo de
ejecución. Por ejemplo, si N = 750 , puede ocurrir que el tamaño de
bloque de 700 u 800 funcione mejor. Más importante aún, no calcules el
tamaño del bloque en tiempo de ejecución, hazlo constante. La división
por constantes está bien optimizada por los compiladores.
391
ESTRUCTURA DE DATOS
392
ESTRUCTURA DE DATOS
394
ESTRUCTURA DE DATOS
3484 - Work for Ten Este ejercicio es similar al 3632 Tobby and Query
su solución parte de la utilización de una estructura de datos RangeTree con
sus respectivas adecuaciones. Ahora el RangeTree lo vamos hacer sobre las
habitaciones y no sobre las personas. La primera adecuación que se tiene
que hacer es cambiar el tipo de dato del RangeTree, en los casos de estudio
siempre se ha visto la estructura trabaja con int ahora vamos a cambiarlo
por un vector de int, serı́a algo como vector<int> ST[MAXTREE] donde
para cada intervalo se almacenara en un vector como máximo los 10 más
inteligentes dentro de ese rango. Las otras adecuaciones que se deben hacer
es los métodos InitTree y Query en el primero :
395
ESTRUCTURA DE DATOS
Luego para cada Ai aplicar los metodos mencionados y capturar los itera-
dores que devuelven dichas funcionales, en caso de iterador que devuelve
upper bounds decrementarlo en uno y mandar a imprimir los valores a que
apunta esos iteradores.
3956 - Street Parade Una vez leido e interpretado el problema nos po-
demos percatar que la calle auxiliar de la que se nos habla en el problema
la podemos ver como una estructura de datos de tipo pila. Y utilizando una
estructura de este tipo podemos resolver el problema de la siguiente manera.
Bien vamos a tener una variable que su valor inicial es 1 y no vas indicar que
carro yo espero que pase. Luego voy por cada carro que entre si su chapa es
la que yo esperaba incremento en uno la variable que me indica la chapa del
carro que yo espero y paso el carro. En caso de que la chapa de carro que
analizo no sea la que espero almaceno dicho carro en la pila. Luego empiezo a
sacar los carros de la pila si cuando saco un carro de la pila su chapa coincide
con la chapa esperada, incremento en uno la chapa esperaba, caso contrario
la respuesta a ese caso es no mientras si logro sacar todos los carros de la
pila sin problema entonces la respuesta para ese caso es yes.
396
ESTRUCTURA DE DATOS
397
ESTRUCTURA DE DATOS
398
Capı́tulo 14
Greedy
14.0.1. Fundamentos
Para que un problema pueda ser solucionado en forma óptima mediante
un algoritmo goloso debe presentar dos caracterı́sticas fundamentales:
399
GREEDY
- Opción golosa. Debe ser posible demostar que una elección localmente
óptima lleva a una solución globalmente óptima. Esta es la clave de
los algoritmos golosos y en general se demuestra por inducción, en pri-
mer lugar se prueba que si existe una solución óptima entonces existe
también una solución que parte de una decisión golosa. Luego para el
subproblema restante por inducción se prueba que se puede llegar a
una solución óptima.
400
GREEDY
2. Para ello trabajará por etapas, tomando en cada una de ellas la decisión
que le parece la mejor, sin considerar las consecuencias futuras, y por
tanto escogerá de entre todos los candidatos el que produce un óptimo
local para esa etapa, suponiendo que será a su vez óptimo global para
el problema.
401
GREEDY
int n ;
struct I n t e r v a l
{
int L , R;
bool operator <(const I n t e r v a l &a ) const
{
i f (R != a .R) return (R < a .R) ;
return (L < a . L) ;
}
};
I n t e r v a l I [MAX N ] ;
i n l i n e int S c h e d u l e I n t e r v a l s ( )
{
s o r t ( I , I+n ) ;
int r e t = 1 ;
402
GREEDY
int currentEnd = I [ 0 ] . R;
f o r ( int i =1; i <n ; i ++)
{
i f ( I [ i ] . L >= currentEnd )
{
currentEnd = I [ i ] . R;
r e t ++;
}
}
return r e t ;
}
int main ( )
{
n = 4;
I [0].L = −1, I [ 0 ] . R = 1 ;
I [1].L = 0 , I [ 1 ] .R = 5;
I [2].L = 2 , I [ 2 ] .R = 3;
I [3].L = 5 , I [ 3 ] .R = 9;
p r i n t f ( " %d\n" , S c h e d u l e I n t e r v a l s ( ) ) ;
return 0 ;
}
403
GREEDY
404
GREEDY
405
GREEDY
Pero entonces, por [4.5], s ha de ser estrictamente menor que m+1, y por
tanto s ≤ m, como querı́amos demostrar.
406
GREEDY
407
GREEDY
Por tanto desde x1 también hay menos de n kilómetros hasta y2 , esto es,
408
GREEDY
409
GREEDY
410
GREEDY
411
GREEDY
2. Maximizar los puntos del equipo cuyo identificador es cero. Esto signi-
fica que voy a declarar como victorias aquellos juegos que tienen pen-
dientes por jugar el equipo 0 para sı́ y derrotas para aquellos equipos
con los cuales debı́a jugar.
3. Ordeno los equipos de acuerdo a la cantidad de puntos acumulados has-
ta el momento de forma descendente. Extraigo de la colección ordenada
de equipos el primer elemento si su identificador no es cero entonces
no es posible que el equipo 0 gane el torneo y la respuesta es N. Si el
equipo es el 0 voy a realizar los siguientes pasos mientras en la colección
exista equipo y el equipo 0 tenga posibilidad de ser campeón.
Extraigo el primer equipo de la colección si sus puntos son menor
que el del equipo 0 y no tiene juego pendientes continuo con el
procedimeinto.
Extraigo el primer equipo de la colección si sus puntos son iguales
o mayor que el del equipo 0 detengo el procedimiento y la respuesta
es N.
Extraigo el primer equipo de la colección si sus puntos son menor
que el del equipo 0 y tiene juego pendientes continuo con el proce-
dimeinto. Primero le vamos a declarar tantas victorias como pueda
se siempre y cuando la cantidad de puntos obtenidos sea menor
que la del equipo 0. Luego si continúa con juegos pendientes le
vamos a declarar tantos empates como se pueda siempre y cuando
la cantidad de puntos obtenidos por el y por el equipo que juegan
con él los empates no supere ni iguale los puntos del equipo 0.
Luego si continúa con juegos pendientes le vamos declarar tantas
derrotas como sea posible siempre y cuando la cantidad de puntos
que obtenga los equipos que le ganen no superen a los puntos del
equipo 0. Luego si equipo analizado tiene todavı́a juegos pendien-
tes entonces el equipo 0 no puede ser campeón sino reordeno los
equipos de nuevo sin tener en cuanta el equipo analizado y repito
el este procedimiento. La hora de efectuar los juegos pendientes
del equipo utilice el orden en que estan los equipos en ese momen-
to para si puede ganar que le gane a los equipos más próximo a
él, si puede empatar que sea con esos equipos luego si tiene que
perder que pierda lo menos posible con esos equipos y si con los
que están con la menor cantidad de puntos.
Si el anterior procedimientos concluyó y todos los equipos pudie-
ron efectuar sus partidos entonces el equipo 0 puede ser campeón
del torneo
412
GREEDY
413
GREEDY
414
Capı́tulo 15
Teorı́a de Grafos
415
TEORÍA DE GRAFOS
416
TEORÍA DE GRAFOS
417
TEORÍA DE GRAFOS
418
TEORÍA DE GRAFOS
419
TEORÍA DE GRAFOS
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
struct Node
{
int d i s t ;
v e c t o r <int> a d j ;
v e c t o r <int> w e i g h t ;
};
Node g r a f [MAX N ] ;
420
TEORÍA DE GRAFOS
struct Edge
{
int a , b ;
int w e i g h t ;
};
Edge E [MAX N ] ;
En este caso las variables a y b de la estructura son los nodos que componen
la aristas mientras la variable weight contiene el valor de la aristas. E es el
arreglo de las aristas del grafo mientras MAX N tiene que ser definida con
valor igual o mayor a la cantidad de aristas del grafos. A continuación les
dejo otra variante para representar los grafos.
421
TEORÍA DE GRAFOS
proceso con cada uno de los hermanos del nodo ya procesado. Análogamente
existe el algoritmo de búsqueda en anchura (BFS o Breadth First Search).
El siguiente ejemplo ilustra el funcionamiento del algoritmo DFS sobre
un grafo de ejemplo. El algoritmo comienza por el nodo 0.
DFS( g r a f o G)
PARA CADA v e r t i c e u que p e r t e n e c e V[G] HACER
e s t a d o [ u ] = NO VISITADO
padre [ u ] = NULO
tiempo = 0
PARA CADA v e r t i c e u que p e r t e n e c e V[G] HACER
SI e s t a d o [ u ] = NO VISITADO ENTONCES
D F S V i s i t a r ( u , tiempo )
422
TEORÍA DE GRAFOS
por una estructura de datos que puede simular la “recursividad” inicial del
algoritmo y disminuye la complejidad del algoritmo. Dicha estructura es una
pila, aplicando los cambios pertinentes el algoritmo quedarı́a como se muestra
en el código.
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
423
TEORÍA DE GRAFOS
424
TEORÍA DE GRAFOS
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
425
TEORÍA DE GRAFOS
- Mientras C es no vacı́o.
426
TEORÍA DE GRAFOS
Este algoritmo fue publicado por primera vez en Proceedings of the Ame-
rican Mathematical Society, pp. 48–50 en 1956, y fue escrito por Joseph
Kruskal. A continuación se muestra un seudocódigo del algoritmo.
f u n c t i o n Kruskal (G)
Para cada v en V[G] h a c e r
Nuevo c o n j u n t o C( v ) \ t e x t l e f t a r r o w {v } .
Nuevo heap Q que c o n t i e n e t o d a s l a s a r i s t a s de G, ordenando
por su p e s o .
D e f i n o un a r b o l T ={ v a c i o }
// n e s e l numero t o t a l de v e r t i c e s
M i e n t r a s T t e n g a menos de n−1 v e r t i c e s h a c e r
( u , v ) i g u a l Q. sacarMin ( )
// p r e v i e n e c i c l o s en T . a g r e g a ( u , v ) s i u y v e s t a n
d i f e r e n t e s componentes en e l c o n j u n t o .
// Notese que C( u ) d e v u e l v e l a componente a l a que
pertenece u .
i f C( v ) d i s t i n t o C( u ) then
Agregar a r i s t a ( v , u ) a T .
Merge C( v ) y C( u ) en e l c o n j u n t o
Return a r b o l T
427
TEORÍA DE GRAFOS
int n , m;
int numComponents , r e t ;
struct Edge
{
int a , b ;
int w e i g h t ;
bool operator <(const Edge &e ) const
{
return ( w e i g h t < e . w e i g h t ) ;
}
};
Edge E [MAX N ] ;
struct Node
{
int p a r e n t ;
int rank ;
};
Node DSU[MAX N ] ;
428
TEORÍA DE GRAFOS
return DSU[ x ] . p a r e n t ;
}
i n l i n e int Kruskal ( )
{
int r e t = 0 , numComponents = n ;
f o r ( int i =0; i <n ; i ++) MakeSet ( i ) ;
s o r t (E , E+m) ;
f o r ( int i =0; i < m && numComponents > 1 ; i ++)
{
i f ( Find (E [ i ] . a ) != Find (E [ i ] . b ) )
{
Union (E [ i ] . a , E [ i ] . b ) ;
r e t += E [ i ] . w e i g h t ;
numComponents−−;
}
}
i f ( numComponents > 1 ) return −1;
return r e t ;
}
int main ( )
{
n = 7 , m = 11;
429
TEORÍA DE GRAFOS
return 0 ;
}
Weight t o t a l = 0 ;
Edges F ;
while (F . s i z e ( ) <n−1 & &! Q. empty ( ) ) {
Edge e = Q. top ( ) ; Q. pop ( ) ;
i f ( uf . unionSet ( e . src , e . dst ) ) {
F . push back ( e ) ;
t o t a l + = e . weight ;
}
}
return p a i r <Weight , Edges> ( t o t a l , F) ;
}
430
TEORÍA DE GRAFOS
431
TEORÍA DE GRAFOS
cuya distancia a los anteriores es mı́nima. Esto significa que en cada paso, las
aristas a considerar son aquellas que inciden en vértices que ya pertenecen
al árbol. El árbol recubridor mı́nimo está completamente construido cuando
no quedan más vértices por agregar.
El siguiente ejemplo ilustra el funcionamiento del algoritmo. La secuencia
de ilustraciones va de izquierda a derecha y de arriba hacia abajo. La primera
imagen muestra el grafo pesado y las siguientes muestran el funcionamien-
to del algoritmo de Prim y como va cambiando el conjunto U durante la
ejecución.
432
TEORÍA DE GRAFOS
Prim ( Grafo G)
/∗ I n i c i a l i z a m o s t o d o s l o s nodos d e l g r a f o .
La d i s t a n c i a l a ponemos a i n f i n i t o y e l padre de cada
nodo a NULL
Encolamos , en una c o l a de p r i o r i d a d
donde l a p r i o r i d a d e s l a d i s t a n c i a ,
t o d a s l a s p a r e j a s <nodo , d i s t a n c i a > d e l g r a f o ∗/
por cada u en V[G] h a c e r
d i s t a n c i a [ u ] = INFINITO
padre [ u ] = NULL
A d i c i o n a r ( c o l a ,<u , d i s t a n c i a [ u] >)
d i s t a n c i a [ u]=0
mientras ! e s t a v a c i a ( cola ) hacer
// OJO: Se e n t i e n d e por mayor p r i o r i d a d a q u e l nodo
cuya d i s t a n c i a [ u ] e s menor .
u = e x t r a e r m i n i m o ( c o l a ) // d e v u e l v e e l minimo y l o
e l i m i n a de l a c o l a .
por cada v a d y a c e n t e a ’u’ h a c e r
s i ( ( v p e r t e n e c e c o l a ) && ( d i s t a n c i a [ v ] > p e s o ( u ,
433
TEORÍA DE GRAFOS
v) ) entonces
padre [ v ] = u
d i s t a n c i a [ v ] = peso (u , v )
A c t u a l i z a r ( c o l a ,<v , d i s t a n c i a [ v ] >)
int n ;
struct Node
{
v e c t o r <int> a d j ;
v e c t o r <int> w e i g h t ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
struct edge
{
int u , v ;
double w ;
bool operator <(const edge &a ) const
{
return (w>a . w) ;
}
};
p r i o r i t y q u e u e <edge> pq prim ;
i n l i n e int Prim ( )
{
int xt = 0 ;
434
TEORÍA DE GRAFOS
int r e t = 0 ;
int amt = 0 ;
while ( amt < n−1)
{
mark [ xt ]= true ;
f o r ( int i =0; i <g r a f [ xt ] . a d j . s i z e ( ) ; i ++)
{
i f ( ! mark [ g r a f [ xt ] . a d j [ i ] ] )
{
edge E ;
E . u = xt ;
E . v = g r a f [ xt ] . a d j [ i ] ;
E . w = g r a f [ xt ] . w e i g h t [ i ] ;
pq prim . push (E) ;
}
}
while ( ! pq prim . empty ( ) )
{
edge X = pq prim . top ( ) ;
pq prim . pop ( ) ;
i f ( ! mark [X. v ] )
{
r e t += X. w ;
xt = X. v ;
amt++;
break ;
}
}
}
return r e t ;
}
int main ( )
{
n = 7;
g r a f [ 1 ] . a d j . push back ( 2 ) ;
g r a f [ 1 ] . w e i g h t . push back ( 8 ) ;
g r a f [ 2 ] . a d j . push back ( 1 ) ;
435
TEORÍA DE GRAFOS
g r a f [ 2 ] . w e i g h t . push back ( 8 ) ;
return 0 ;
}
436
TEORÍA DE GRAFOS
de aristas.
La siguiente variante devuelve las aristas que conforman el árbol de ex-
pansión mı́nima que conforma el algoritmo.
v e c t o r <bool> v i s i t e d ( n ) ;
p r i o r i t y q u e u e <Edge> Q;
Q. push ( Edge (− 1 , r , 0 ) ) ;
while ( ! Q. empty ( ) )
{
Edge e = Q. top ( ) ;
Q. pop ( ) ;
i f ( v i s i t e d [ e . dst ] )
continue ;
T . push back ( e ) ;
t o t a l+=e . w e i g h t ;
v i s i t e d [ e . d s t ] = true ;
FOR ( f , g [ e . d s t ] )
i f ( ! v i s i t e d [ f −>d s t ] )
Q. push ( ∗ f ) ;
}
return p a i r <Weight , Edges> ( t o t a l , T) ;
}
437
TEORÍA DE GRAFOS
438
TEORÍA DE GRAFOS
DIJKSTRA ( Grafo G, n o d o f u e n t e s )
para u que p e r t e n e c e V[G] h a c e r
d i s t a n c i a [ u ] = INFINITO
padre [ u ] = NULL
visto [ u ] = false
distancia [ s ] = 0
adicionar ( cola , ( s , d i s t a n c i a [ s ] ) )
m i e n t r a s que c o l a no e s v a c i a h a c e r
u = extraer minimo ( cola )
v i s t o [ u ] = true
para t o d o s v p e r t e n e c e a d y a c e n c i a [ u ] h a c e r
s i no v i s t o [ v ] y d i s t a n c i a [ v ] > d i s t a n c i a [ u ] +
peso (u , v ) hacer
d i s t a n c i a [ v ] = d i s t a n c i a [ u ] + peso (u , v )
padre [ v ] = u
adicionar ( cola , ( v , d i s t a n c i a [ v ] ) )
f u n c i o n D i j k s t r a ( Grafo G, n o d o s a l i d a s )
// Usaremos un v e c t o r para g u a r d a r l a s d i s t a n c i a s d e l nodo
salida al resto
entero distancia [ n ]
// I n i c i a l i z a m o s e l v e c t o r con d i s t a n c i a s i n i c i a l e s
booleano v i s t o [ n ]
// v e c t o r de b o l e a n o s para c o n t r o l a r l o s v e r t i c e s de l o s que ya
tenemos l a d i s t a n c i a minima
para cada w que p e r t e n e c e V[G] h a c e r
S i ( no e x i s t e a r i s t a e n t r e s y w) e n t o n c e s
d i s t a n c i a [ w ] = I n f i n i t o // puedes marcar l a c a s i l l a con
un −1 por e j e m p l o
Si no
d i s t a n c i a [ w ] = p e s o ( s , w)
fin si
439
TEORÍA DE GRAFOS
f i n para
distancia [ s ] = 0
visto [ s ] = cierto
//n e s e l numero de v e r t i c e s que t i e n e e l Grafo
m i e n t r a s que ( n o e s t e n v i s t o s t o d o s ) h a c e r
v e r t i c e = c o g e r e l m i n i m o d e l v e c t o r d i s t a n c i a y que no
este visto ;
visto [ vertice ] = cierto ;
para cada w que p e r t e n e c e s u c e s o r e s (G, v e r t i c e ) h a c e r
s i d i s t a n c i a [ w]> d i s t a n c i a [ v e r t i c e ]+ p e s o ( v e r t i c e , w)
entonces
d i s t a n c i a [ w ] = d i s t a n c i a [ v e r t i c e ]+ p e s o ( v e r t i c e , w)
fin si
f i n para
f i n mientras
f i n funcion .
440
TEORÍA DE GRAFOS
int n ;
struct Node
{
int d i s t ;
v e c t o r <int> a d j ;
v e c t o r <int> w e i g h t ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
struct p q e n t r y
{
int node , d i s t ;
bool operator <(const p q e n t r y &a ) const
{
i f ( d i s t != a . d i s t ) return ( d i s t > a . d i s t ) ;
return ( node > a . node ) ;
}
};
i n l i n e void D i j k s t r a ( int s o u r c e )
{
p r i o r i t y q u e u e <p q e n t r y > pq ;
pq entry P;
f o r ( int i =0; i <n ; i ++)
{
i f ( i == s o u r c e )
{
graf [ i ] . dist = 0;
P . node = i ;
P. dist = 0;
pq . push (P) ;
}
441
TEORÍA DE GRAFOS
e l s e g r a f [ i ] . d i s t = INF ;
}
while ( ! pq . empty ( ) )
{
p q e n t r y c u r r = pq . top ( ) ;
pq . pop ( ) ;
int nod = c u r r . node ;
int d i s = c u r r . d i s t ;
f o r ( int i =0; i <g r a f [ nod ] . a d j . s i z e ( ) ; i ++)
{
i f ( ! mark [ g r a f [ nod ] . a d j [ i ] ] )
{
int nextNode = g r a f [ nod ] . a d j [ i ] ;
i f ( d i s + g r a f [ nod ] . w e i g h t [ i ] < g r a f [ nextNode ] .
dist )
{
g r a f [ nextNode ] . d i s t = d i s + g r a f [ nod ] . w e i g h t
[ i ];
P . node = nextNode ;
P . d i s t = g r a f [ nextNode ] . d i s t ;
pq . push (P) ;
}
}
}
mark [ nod ] = true ;
}
}
int main ( )
{
n = 4;
g r a f [ 3 ] . a d j . push back ( 1 ) ;
g r a f [ 3 ] . w e i g h t . push back ( 6 ) ;
442
TEORÍA DE GRAFOS
g r a f [ 1 ] . a d j . push back ( 3 ) ;
g r a f [ 1 ] . w e i g h t . push back ( 6 ) ;
Dijkstra (0) ;
p r i n t f ( " %d\n" , g r a f [ 3 ] . d i s t ) ;
return 0 ;
}
443
TEORÍA DE GRAFOS
int s: Nodo inicial del cual se desea calcular la distancia mı́nima desde
el hacia cualquier nodo grafo.
444
TEORÍA DE GRAFOS
ausencia de ciclos negativos, el camino más corto solo visita cada vértice una
vez. A diferencia de la solución voraz, la cual depende de la suposición de
que los pesos sean positivos, esta solución se aproxima más al caso general.
Existen dos versiones:
445
TEORÍA DE GRAFOS
u = e x t r a e r (Q)
e n c o l a [ u]=FALSE
// r e l a j a m o s l a s a r i s t a s
f o r v i n ady [ u ] do
i f d i s t a n c i a [ v]> d i s t a n c i a [ u ] + p e s o ( u , v )
then
d i s t a n c i a [ v ] = d i s t a n c i a [ u ] + peso (u , v )
padre [ v ] = u
i f e n c o l a [ v]==FALSE then
e n c o l a r ( v , Q)
e n c o l a [ v]=TRUE
446
TEORÍA DE GRAFOS
int v , e ;
int d i s t [MAX N ] ;
struct Edge
{
int x , y , w e i g h t ;
};
Edge E [MAX N ] ;
int main ( )
{
v = 4 , e = 8;
E [ 0 ] . x = 0 , E [ 0 ] . y = 1 , E [ 0 ] . weight = 5 ;
E [ 1 ] . x = 1 , E [ 1 ] . y = 0 , E [ 1 ] . weight = 5 ;
E [ 2 ] . x = 1 , E [ 2 ] . y = 2 , E [ 2 ] . weight = 5 ;
E [ 3 ] . x = 2 , E [ 3 ] . y = 1 , E [ 3 ] . weight = 5 ;
E [ 4 ] . x = 2 , E [ 4 ] . y = 3 , E [ 4 ] . weight = 5 ;
E [ 5 ] . x = 3 , E [ 5 ] . y = 2 , E [ 5 ] . weight = 5 ;
E [ 6 ] . x = 3 , E [ 6 ] . y = 1 , E [ 6 ] . weight = 6 ;
447
TEORÍA DE GRAFOS
E [ 7 ] . x = 1 , E [ 7 ] . y = 3 , E [ 7 ] . weight = 6 ;
BellmanFord ( 0 ) ;
p r i n t f ( " %d\n" , d i s t [ 3 ] ) ;
return 0 ;
}
448
TEORÍA DE GRAFOS
i n l i n e void FloydWarshall ( )
{
f o r ( int i =1; i<=n ; i ++)
{
f o r ( int j =1; j<=n ; j ++)
{
flojd [ i ] [ j ] = dist [ i ] [ j ] ;
}
flojd [ i ] [ i ] = 0;
}
f o r ( int k=1;k<=n ; k++)
{
f o r ( int i =1; i<=n ; i ++)
{
f o r ( int j =1; j<=n ; j ++)
{
if ( flojd [ i ][ k] + flojd [k ][ j ] < flojd [ i ][ j ])
{
flojd [ i ][ j ] = flojd [ i ][ k] + flojd [k ][ j ];
}
}
}
}
}
Donde dist es la matriz de distancia del grafo, una vez ejecutado el al-
goritmo la distancia mı́nima entre cualquier par x,y va estar en flojd[x][y].
Como podemos analizar el algoritmo tiene una complejidad de N3 donde N
es la cantidad de nodos, lo que hace que este algoritmo sea factible su uti-
lización cuando el numero de de nodos sea menor igual a 100 nodos lo que
significa en un millón de operaciones por parte del algoritmo.
El algoritmo de Floyd-Warshall puede ser utilizado para resolver los si-
guientes problemas, entre otros:
449
TEORÍA DE GRAFOS
450
TEORÍA DE GRAFOS
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
int i n d e g r e e [MAX N ] ;
int t o p o s o r t [MAX N ] ;
i n l i n e int TopoSort ( )
{
queue<int> S ;
f o r ( int i =0; i <n ; i ++) i f ( i n d e g r e e [ i ] == 0 ) S . push ( i ) ;
int i d x = 0 ;
while ( ! S . empty ( ) )
{
int i d d = S . f r o n t ( ) ;
S . pop ( ) ;
t o p o s o r t [ i d x ++] = i d d ;
f o r ( int i =0; i <g r a f [ i d d ] . a d j . s i z e ( ) ; i ++)
{
i f (−− i n d e g r e e [ g r a f [ i d d ] . a d j [ i ] ] == 0 ) S . push ( g r a f [
idd ] . adj [ i ] ) ;
}
}
i f ( idx < n)
return −1; // C i c l o d e t e c t a d o !
else
return 0 ;
}
Donde n es la cantidad de nodos que tiene el grafo sobre el cual vamos apli-
car el ordenamiento topológico, graf es lo que utilizaremos para representar
nuestro grafo es una estructura que para el nodo i tiene un vector con los
nodos adyacentes a él. El arreglo indegree se va almacenar en la inésima la
cantidad de aristas incidentes ( si la arista termina en ese nodo, no se cuenta
si la arista parte de él) sobre el inésimo nodo del grafo. El arreglo toposort
estarán los nodos en orden topológico siempre y cuando la ejecución del algo-
ritmo devuelva como valor 0 en caso de que su valor de retorno sea -1 menos
significa que el grafo tiene un ciclo lo que significa que no se puede hacer un
ordenamiento topológico sobre el grafo.
Como podemos apreciar el ordenamiento topológico solo se puede obtener
cuando el grafo no presenta ciclos. La complejidad del algoritmo el O(E+V)
donde E es la cantidad de aristas y V la cantidad de nodos del grafo. Tener
presente que un grafo puede tener multiple ordenamientos topologicos todo
depende del orden en que se seleccione los nodos del grafo.
451
TEORÍA DE GRAFOS
452
TEORÍA DE GRAFOS
int n ;
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
453
TEORÍA DE GRAFOS
}
}
return r e t ;
}
int main ( )
{
n = 6;
g r a f [ 0 ] . a d j . push back ( 1 ) ;
g r a f [ 1 ] . a d j . push back ( 0 ) ;
g r a f [ 0 ] . a d j . push back ( 2 ) ;
g r a f [ 2 ] . a d j . push back ( 0 ) ;
g r a f [ 4 ] . a d j . push back ( 4 ) ;
g r a f [ 3 ] . a d j . push back ( 5 ) ;
g r a f [ 5 ] . a d j . push back ( 3 ) ;
return 0 ;
}
454
TEORÍA DE GRAFOS
Cualquier otro caso va oscilar en este rango definido por estos dos ca-
sos explicados anteriormenente. Es por eso que en el peor de los casos este
algoritmo va tener una complejidad de O(V+E).
455
TEORÍA DE GRAFOS
R e s u l t t = v i s i t (− 1 , r . second , g ) ;
return t . f i r s t ; // ( r . second , t . s e c o n d ) i s f a r t h e s t p a i r
}
La siguiente es otra implementación donde los nodos del grafos son enu-
merados de 0 a N-1.
struct Node
{
int m d i s t ;
v e c t o r <int> m n e i g h b o r s ;
int m dfs ;
Node ( )
{
m neighbors . c l e a r ( ) ;
m dfs =0;
m d i s t =0;
}
};
Node g r a p h s [MAX] ;
int d i a m e t e r T r e e ( int n o d e S t a r t )
{
int tDiameter =0;
int nextNode , currentNode , f a r t h e s t N o d e= n o d e S t a r t ;
g r a p h s [ n o d e S t a r t ] . m d i s t =0;
g r a p h s [ n o d e S t a r t ] . m dfs =1;
s t a c k <int> v i s i t ;
v i s i t . push ( n o d e S t a r t ) ;
while ( ! v i s i t . empty ( ) )
{
currentNode= v i s i t . top ( ) ;
v i s i t . pop ( ) ;
i f ( g r a p h s [ currentNode ] . m dist >tDiameter )
{
tDiameter=g r a p h s [ currentNode ] . m d i s t ;
f a r t h e s t N o d e=currentNode ;
}
456
TEORÍA DE GRAFOS
g r a p h s [ f a r t h e s t N o d e ] . m d i s t =0;
g r a p h s [ f a r t h e s t N o d e ] . m dfs =2;
tDiameter =0;
v i s i t . push ( f a r t h e s t N o d e ) ;
while ( ! v i s i t . empty ( ) )
{
currentNode= v i s i t . top ( ) ;
v i s i t . pop ( ) ;
i f ( g r a p h s [ currentNode ] . m dist >tDiameter )
{
tDiameter=g r a p h s [ currentNode ] . m d i s t ;
f a r t h e s t N o d e=currentNode ;
}
457
TEORÍA DE GRAFOS
int n ;
458
TEORÍA DE GRAFOS
struct Node
{
v e c t o r <int> a d j ;
};
Node g r a f [MAX N ] ;
bool mark [MAX N ] ;
i n l i n e bool h a s C y c l e ( int n )
{
f o r ( int i =0; i <n ; i ++)
{
i f ( ! mark [ i ] )
{
i f (DFS( i ) ) return true ;
}
}
return f a l s e ;
}
int main ( )
459
TEORÍA DE GRAFOS
{
n = 6;
g r a f [ 0 ] . a d j . push back ( 1 ) ;
g r a f [ 1 ] . a d j . push back ( 0 ) ;
g r a f [ 0 ] . a d j . push back ( 2 ) ;
g r a f [ 2 ] . a d j . push back ( 0 ) ;
g r a f [ 2 ] . a d j . push back ( 1 ) ;
g r a f [ 1 ] . a d j . push back ( 2 ) ;
g r a f [ 3 ] . a d j . push back ( 5 ) ;
g r a f [ 5 ] . a d j . push back ( 3 ) ;
p r i n t f ( " %d\n" , h a s C y c l e ( n ) ? 1 : 0 ) ;
return 0 ;
}
460
TEORÍA DE GRAFOS
Vertices: 1 2 5 2 6 2 1 3 1 4 7 4 1
Heights: 1 2 3 2 3 2 1 2 1 2 3 2 1
461
TEORÍA DE GRAFOS
struct LCA {
v e c t o r <int> h e i g h t , e u l e r , f i r s t , s e g t r e e ;
v e c t o r <bool> v i s i t e d ;
int n ;
462
TEORÍA DE GRAFOS
463
TEORÍA DE GRAFOS
464
TEORÍA DE GRAFOS
Esto nos conduce a plantear que sea el grafo G=(V, E) conexo y no diri-
gido. La raı́z tiene, al menos, dos hijos en árbol abarcador en profundidad es
un punto de articulación. Lo anterior es se demuestra de la siguiente manera.
465
TEORÍA DE GRAFOS
Donde las aristas rojas forman parte del árbol de recorrido en profundidad
mientras.
En este esquema nos podemos percatar que los nodos C y E que son
los puntos de articulación tienen, al menos, un hijo, tal que; no existe una
arista de retroceso (lı́neas negras discontinua) desde desde ese hijo, o desde
cualquiera de sus descendientes, hacia un ancestro de C o E en el árbol de
recorrido en profundidad.
Esta observación nos permite decir que sea G=(V, E) conexo y no dirigido.
Un vértice v que no sea raı́z del árbol abarcador en profundidad es punto de
articulación tiene, al menos, un hijo w tal que no existe una arista de retroceso
desde w , o desde cualquiera de sus descendientes, hacia un ancestro de v.
466
TEORÍA DE GRAFOS
2. Se establece también el array low[v] tal que: low[v] = min (d[v], d[w]),
1 ≤ v ≤ |V |, w: es cualquier vértice, alcanzable desde v, al cual se
llega bajando, cero o más niveles, por cualquier rama que salga de
467
TEORÍA DE GRAFOS
struct Node{
v e c t o r <int> a d j ;
};
int nadj=graph [ u ] . a d j . s i z e ( ) ;
v e c t o r <int> a r t i c u l a t i o n P o i n t s ( ) {
v e c t o r <int> i n d e x P o i n t s ;
f i l l ( mark , mark+MAX, f a l s e ) ;
t i m e s =0;
d f s (1 , −1 , i n d e x P o i n t s ) ;
return i n d e x P o i n t s ;
}
468
TEORÍA DE GRAFOS
#define SZ 100
bool M[ SZ ] [ SZ ] ;
int N, c o l o u r [ SZ ] , dfsNum [ SZ ] , num , pos [ SZ ] , l e a s t A n c e s t o r [ SZ ] ,
p a r e n t [ SZ ] ;
int d f s ( int u ) {
int ans = 0 , c o n t = 0 , v ;
s t a c k <int> S ;
S . push ( u ) ;
while ( ! S . empty ( ) ) {
v=S . top ( ) ;
i f ( c o l o u r [ v]== 0 ) {
colour [ v ]=1;
dfsNum [ v]=num++;
l e a s t A n c e s t o r [ v]=num ;
}
f o r ( ; pos [ v]<N;++pos [ v ] ) {
i f (M[ v ] [ pos [ v ] ] && pos [ v ] ! = p a r e n t [ v ] ) {
i f ( c o l o u r [ pos [ v]]== 0 ) {
p a r e n t [ pos [ v ] ] = v ;
S . push ( pos [ v ] ) ;
i f ( v==u )++c o n t ;
break ;
} else
l e a s t A n c e s t o r [ v ] < ? = dfsNum [ pos [ v ] ] ;
}
}
i f ( pos [ v]==N) {
colour [ v ]=2;
469
TEORÍA DE GRAFOS
S . pop ( ) ;
i f ( v!=u ) l e a s t A n c e s t o r [ p a r e n t [ v ] ] < ? = l e a s t A n c e s t o r [ v
];
}
}
i f ( cont >1){
++ans ;
p r i n t f ( " %d\n" , u ) ;
}
void A r t i c u l a t i o n points () {
memset ( c o l o u r , 0 , sizeof ( colour ) ) ;
memset ( pos , 0 , s i z e o f ( pos ) ) ;
memset ( parent , −1, s i z e o f ( p a r e n t ) ) ;
num = 0 ;
int t o t a l = 0 ;
f o r ( int i = 0 ; i < N; ++i )
i f ( c o l o u r [ i ] == 0 ) t o t a l += d f s ( i ) ;
470
TEORÍA DE GRAFOS
int n ;
v e c t o r <v e c t o r <int>> a d j ;
v e c t o r <int> s i d e ( n , −1) ;
bool i s b i p a r t i t e = true ;
queue<int> q ;
f o r ( int s t =0; s t <n;++ s t ) {
i f ( s i d e [ s t ]==−1){
q . push ( s t ) ;
side [ st ] = 0;
while ( ! q . empty ( ) ) {
int v=q . f r o n t ( ) ;
q . pop ( ) ;
f o r ( int u : a d j [ v ] ) {
i f ( s i d e [ u]==−1){
side [u] = side [ v ] ˆ 1
q . push ( u ) ;
} else {
i s b i p a r t i t e &= s i d e [ u ] != s i d e [ v ] ;
}
}
}
}
}
471
TEORÍA DE GRAFOS
472
TEORÍA DE GRAFOS
de conocer que elementos de ella son parte del borde exterior y cuanto aporta
este al perı́metro.
2713 - Poisonous Gas Un simple BFS comenzando por el nodo uno cuyo
costo es uno a los vecinos a este es dos y ası́ sucesivamente tener cuidado
porque puede existir nodos que no estén conectado con la componente conexa
a la que está asociada el nodo #1 para estos nodos el valor es -1.
474
TEORÍA DE GRAFOS
en este DFS visitaremos de cada estudiante al estudiante que él sigue (sino
no está visitado) y luego a los estudiantes que lo siguen.
475
TEORÍA DE GRAFOS
476
TEORÍA DE GRAFOS
int j j = lookup ( j ) ;
i f ( i i == j j )
return ;
if ( ii < jj )
{
relations [ jj ] = ii ;
cantFriends [ i i ] += c a n t F r i e n d s [ j j ] ;
}
else
{
relations [ ii ] = jj ;
cantFriends [ j j ] += c a n t F r i e n d s [ i i ] ;
}
}
2738 - Coco-Bits and the Spies Network Una vez leı́do el problema
no cabe duda que nos estan pidiendo el árbol de expasión mı́nima del grafo
donde los nodos son los puntos de los espı́as y las aristas las conexiones entre
los espı́as donde el peso de las aristas es la distancia euclidiana entre los dos
puntos conocidos las coordenadas de cada uno. El algoritmo ideal para esto
es el Kruskal pero hay un detalle. El problema te obliga que determinados
espı́as esten conectados de la forma punto a punto como se dice en el argot
informático, eso significa que mi árbol de expasión minı́ma debe incluir dichas
aristas aunque inicialemnte las puede bien no considerar. Es por eso que se
debe realizar determinada adecuaciones al Kruskal. Lo primero es tomar
para K1 y K2 las distancia entre ellos y acumularla para la respuesta y si no
están en la misma componente conexa hasta ese momento, ponerlos (Usar la
estructura Disjoint Set Union que usa el Kruskal), luego con el resto de las
aristas realizar el Kruskal hasta que la cantidad de componentes conexa del
árbol de expasión mı́nima sea una. Devolver la cantidad de longitud deseada.
2571 - Crazy Frog Para asegurar que el salto de la Rana Loca sea lo
mı́nimo posible debemos construir un árbol de expasión mı́nimo del grafo
donde los nodos son las piedras y las aristas las conexiones entre las piedras
donde el peso de las aristas es la distancia euclidiana entre los dos puntos
477
TEORÍA DE GRAFOS
2081 - Saving Money Para responder cada query del problema basta con
realizar previamente un floyd sobre el grafo dado. Tenga en cuenta que puede
existir multiples aristas entre dos nodos.
478
TEORÍA DE GRAFOS
479
TEORÍA DE GRAFOS
que cada uno realiza una tarea y cada tarea es realizada por un único traba-
jador. Se resuelve aplicando el algoritmo húngaro o algoritmo Kuhn-Munkres
como también es conocido.
480
TEORÍA DE GRAFOS
3928 - The Deep Dark Web La idea para solucionar el primer subpro-
blema es usar minimum spanning tree como kruskal, se ordenan las aristas
por su peso de forma descendente y para dar solución del primer subproble-
ma nos quedamos con el peso de la arista que hace que los dos nodos (S y
T) estén en la misma componente, y para el segundo subproblema se debe
hallar el camino mı́nimo entre S y T usando dijkstra donde el costo de una
arista es 0 si su valor original es mayor que el valor que se desea obtener y si
no es la diferencia de cuanto debe aumentar para alcanzar el valor deseado.
481
TEORÍA DE GRAFOS
del árbol, incrementarlo en uno( vamos asumir que el peso de cada arista es
uno, por tanto un diámetro de 4 significa que es un camino con cinco nodos,
que en nuestro problema son los castillos ) y esa será la respuesta
482
TEORÍA DE GRAFOS
2668 - Tom and Jerry II El problema nos plantea que dado un registro
que almacena un valor A se desea saber la menor secuencia de operaciones
aritmética (suma, resta, división entera y multiplicación) para alcanzar al-
macenar dicho registro un valor B en caso que no se puede la respuesta es
’ ?’. Para solucionar el problema vamos a realizar BFS partiendo del valor A
para llegar a B realizando suma, multiplicación y división entera. La resta
no la utilizamos porque A-A me darı́a 0 y caerı́a un valor del cual no saldrı́a
con alguna operación de las permitidas. Llevar un set o map de los números
analizados para no repetir análisis con el mismo número. En caso de existir
varias soluciones validas vamos a tomar aquella que tenga la menor secuencia
de operaciones y que lexicograficamente sea menor que las demás.
1220 - Come and Go Una vez leı́do el problema nos podemos percartar
que nos pide deteminar dado un conjunto de N intersecciones si es posible ir
entre cualquiera par de intersecciones en un sentido y en el otro. Para esto
las aristas que conecta a la intersecciones son de dos tipos, de un solo sentido
o doble sentido. Si construimos el grafo con las restricciones planteadas y
realizamos un recorrido a lo ancho sobre el grafo y todos los nodos del grafo
son visitados en una sola componente conexa. La respuesta es 1 sino es 0.
3955 - Water Among Cubes Una vez leı́do el problema nos podemos
percatar que nos piden determinar el maximo volumen de agua que se puede
contener dentro de la malla de cuadriculas donde de las cuadriculas tienen
una altura determinada. Para empezar vamos asumir que cada cuadricula de
la malla puede ser de dos tipos: MURO y CONTENEDOR. En el primer tipo
estarán aquellas cuadriculas que conforman los muros que contendrán el agua
que almacenarán , mientras en el segundo tipo están aquellas que se va verte
agua sobre ellas. Al principio vamos asumir que todas las cuadriculas de la
malla son de tipo CONTENEDOR. Luego cada cuadricula del borde de la
malla vamos a asumir que es de tipo MURO porque a priori las cuadriculas del
borde de la malla van ser parte del muro contenedor. Luego cada cuadricula
interna (no borde de la malla) vamos analizarla porque si tiene una altura
mayor o igual a la cuadriculas del borde entonces en esa cuadricula no se
puede verter agua en ella porque cuando en esa cuadricula cuando se contega
agua se habrá botado por un o alguno de los bordes.
Entonces una vez visto esto, el analisis de las cuadriculas internas debe
ser siguiente. Las alamcenos en una colección y las ordenos por sus alturas
de forma descendente. Para cada cuadricula se realiza un BFS partiendo de
la posición de dicha cuadricula y puede pasar otra posición siempre y cuando
la altura de la cuadricula hacia donde se vaya desplazar sea menor e igual
483
TEORÍA DE GRAFOS
struct F i e l d
{
TypeField m type ;
int m row ;
int m column ;
ULL m heigth ;
ULL m minHeigthWalk ;
....
484
TEORÍA DE GRAFOS
....
bool isWalk ( F i e l d f i e l d )
{
bool i s C o n t a i n e r=true ;
C e l l at , next ;
queue<C e l l > v i s i t ;
ULL heigthMin= f i e l d . m heigth ;
v i s i t . push ( C e l l ( f i e l d . m row , f i e l d . m column ) ) ;
memset ( mark , f a l s e , s i z e o f ( mark ) ) ;
485
TEORÍA DE GRAFOS
{
matrix [ f i e l d . m row ] [ f i e l d . m column ] . m minHeigthWalk=
min ( matrix [ f i e l d . m row ] [ f i e l d . m column ] .
m minHeigthWalk ,
matrix [ next . m row ] [ next . m column ] . m minHeigthWalk ) ;
}
}
}
}
}
}
return ( ! i s C o n t a i n e r ) ;
}
.....
void s o l u t i o n ( )
{
ULL maxVolumen=0;
REP1 N( i , row )
{
REP1 N( j , column )
i f ( matrix [ i ] [ j ] . m type==CONTAINER)
maxVolumen+=
( matrix [ i ] [ j ] . m minHeigthWalk−matrix [ i ] [ j ] . m heigth ) ;
}
cout<<maxVolumen<<ENDL;
}
486
TEORÍA DE GRAFOS
4088 - How Long is the Path Una vez leı́do el problema podemos per-
catarnos que nos pide determinar el camino mas largo donde todos los nodos
que se visite en en ese camino sean nodos de color rojo. Si analizamos y
representamos el graficamente el caso de ejemplo que nos pones el problema
se nos reduce en encontrar aquel subárbol solo conformado por nodos rojos
cuyo dı́ametro sea mayor y devolver dicho diametro incrementado en uno y
asumiendo que el peso de cada arista es 1. Ahora veamos algunos detalles
que pueden optimizar nuestra solución.
3949 - Jumping of the Knight Una vez leı́do el problema nos podemos
percatar que nos piden determinar si dada dos posiciones en un tablero de
ajedrez se puede ir de una otra casilla utilizando una pieza de tipo caballo
487
TEORÍA DE GRAFOS
pero con la restricción de movimiento que la pieza solo puede ir a una celda
cuya fila sea mayor que la fila de la posición actual. Para resolver este pro-
blema solo debemos aplicar un BFS tomando como posición inicial la celda
inicial y luego de terminada la ejecucción del BFS ver si la celda donde debe
terminar la pieza fue visitada. Se debe tener en cuenta a la hora de generar
las celdas vecinas la restricción de movimiento de la pieza.
4111 - Training the Deivi Una vez leı́do el problema nos podemos per-
catar que se puede modelar a través de una grafo donde los laboratorios
serı́an los nodos y los pasillos las aristas. Ahora una vez hecha esta modela-
ción debemos ver el problema radica en encontrar el camino mas corto o con
el mı́nimo costo desde el labortorio 1 hasta el laboratorio N. Aplicando un
Dijkstra desde el laboratorio 1 hacia los demás laboratorios. Una vez con-
cluido la ejecucción del algoritmo solo debemos imprimir la distancia mı́nima
calculada por el algoritmo para el nodo N.
488
TEORÍA DE GRAFOS
489
TEORÍA DE GRAFOS
2575 - Trees El problema nos pide hallar la cantidad de árboles que existen
en un grafo del cual conocemos la cantidad de nodos y las aristas que lo
componen. Para resolver el problema vamos hallar todas las componentes
conexas que tiene el mismo. De las componentes conexas halladas vamos a
contar aquellas componentes conexas cuya estructura forman un árbol. Para
verificar si la componente conexa forma un árbol basta con chequear que no
exista ciclo en ella. Para agilizar este proceso podemos utilizando un DFS
para determinar la componente conexa y a su vez utilizarlo para detectar
si existe ciclos en ella. Luego solo debemos imprimir la cantidad de árboles-
componente conexas halladas en el formato especificado en el ejercicio.
490
Capı́tulo 16
Misceláneas
491
MISCELÁNEAS
Clase Date
La clase Date representa un instante de tiempo dado con precisión de
milisegundos. La información sobre fecha y hora se almacena en un entero
long de 64 bits que contiene los milisegundos transcurridos desde las 00:00:00
del 1 de enero de 1970 GMT (Greenwich mean time). Ya se verá que otras
clases permiten a partir de un objeto Date obtener información del año, mes,
dı́a, horas, minutos y segundos. A continuación se muestran los métodos de
la clase Date, habiéndose eliminado los métodos declarados obsoletos (depre-
cated) en el JDK 1.2:
492
MISCELÁNEAS
public j a v a . l a n g . S t r i n g t o S t r i n g ( ) ;
}
493
MISCELÁNEAS
La variable int DAY OF WEEK puede tomar los valores int SUNDAY,
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY y SA-
TURDAY.
La variable int MONTH puede tomar los valores int JANUARY, FE-
BRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEP-
TEMBER, OCTOBER, NOVEMBER, DECEMBER. Para hacer los
programas más legibles es preferible utilizar estas constantes simbóli-
cas que los correspondientes números del 0 al 11.
494
MISCELÁNEAS
protected long g e t T i m e I n M i l l i s ( ) ;
public j a v a . u t i l . TimeZone getTimeZone ( ) ;
public f i n a l boolean i s S e t ( int ) ;
public void r o l l ( int , int ) ;
public abstract void r o l l ( int , boolean ) ;
public f i n a l void s e t ( int , int ) ;
public f i n a l void s e t ( int , int , int ) ;
public f i n a l void s e t ( int , int , int , int , int ) ;
public f i n a l void s e t ( int , int , int , int , int , int ) ;
public f i n a l void setTime ( j a v a . u t i l . Date ) ;
public void setFirstDayOfWeek ( int ) ;
protected void s e t T i m e I n M i l l i s ( long ) ;
public void setTimeZone ( j a v a . u t i l . TimeZone ) ;
public j a v a . l a n g . S t r i n g t o S t r i n g ( ) ;
}
public c l a s s PruebaFechas {
public s t a t i c void main ( S t r i n g a r g [ ] ) {
Date d = new Date ( ) ;
G r e g o r i a n C a l e n d a r gc = new G r e g o r i a n C a l e n d a r ( ) ;
gc . setTime ( d ) ;
System . out . p r i n t l n ( "Era: "+gc . g e t ( Calendar .ERA) ) ;
System . out . p r i n t l n ( "Year: "+gc . g e t ( Calendar .YEAR) ) ;
System . out . p r i n t l n ( " Month : "+gc . g e t ( Calendar .MONTH) ) ;
System . out . p r i n t l n ( "Dia del mes: "+gc . g e t ( Calendar .DAY OF MONTH)
);
System . out . p r i n t l n ( "D de la S en mes:" +gc . g e t ( Calendar .
495
MISCELÁNEAS
import j a v a . u t i l . ∗ ;
import j a v a . t e x t . ∗ ;
c l a s s SimpleDateForm {
public s t a t i c void main ( S t r i n g a r g [ ] ) throws P a r s e E x c e p t i o n {
SimpleDateFormat s d f 1 = new SimpleDateFormat ( "dd -MM -yyyy hh:mm:
ss" ) ;
SimpleDateFormat s d f 2 = new SimpleDateFormat ( "dd -MM -yy" ) ;
Date d = s d f 1 . p a r s e ( "12 -04 -1968 11:23:45 " ) ;
S t r i n g s = s d f 2 . format ( d ) ;
System . out . p r i n t l n ( s ) ;
}
}
496
MISCELÁNEAS
497
MISCELÁNEAS
16.2. Ajedrez
16.2.1. Rey
16.2.2. Dama
16.2.3. Torre
16.2.4. Alfil
El alfil es una pieza menor del ajedrez occidental de valor aproximado de
tres peones. Se mueve en diagonal y no pueden saltar piezas intervinientes,
y captura tomando el lugar ocupado por la pieza adversaria. Debido a las
caracterı́sticas de su movimiento tiene la deficiencia de la debilidad del color
donde su movimiento queda limitado al color del escaque en la que se inicia
la partida.
498
MISCELÁNEAS
Figura 16.1: Movimiento del alfil, los cı́rculos indican donde se permite el
movimiento.
16.2.5. Caballo
El caballo es una pieza menor del ajedrez occidental de un valor aproxi-
mado de tres peones. Tiene un movimiento semejante a una L y, a diferencia
de otras piezas, puede saltar piezas intermedias. Captura tomando el escaque
ocupado por la pieza adversaria.
499
MISCELÁNEAS
BFS. Pero que hacer cuando dicho algoritmo es ineficiente producto de que
no conocemos las dimensiones del tablero o este es infito. Por suerte existe un
algoritmo que dado dos posiciones del tablero el cual se asume que es infito
determina la cantidad de mı́nima que debe dar un caballo para ir de una a
otra posición. A continuación les dejo el código.
#include <cmath>
#include <math . h>
#include <a l g o r i t h m >
#define LL long long
LL d i s t (LL x1 , LL y1 , LL x2 , LL y2 )
{
LL dx=abs ( x2−x1 ) ;
LL dy=abs ( y2−y1 ) ;
LL l b =(dx+1) / 2 ;
l b=max( lb , ( dy+1) / 2 ) ;
l b=max( lb , ( dy+dx+2) / 3 ) ;
500
MISCELÁNEAS
return l b ;
}
Peón
501
MISCELÁNEAS
la solución para resolver cada caso de prueba y aquı́ puede venir dos posibles
veredictos de acuerdo a jurado que se utilice. Si tu solución en algún caso
especificó se demora más de lo permitido el veredicto será Lı́mite de tiempo
excedido, algunos jurados se toma además el tiempo total que se demoró
tu solución para solucionar todos los casos. Si ese tiempo es superior a una
cota definida para el problema entonces el veredicto será el de Lı́mite de
tiempo total excedido.
Luego si despues de todas estas comprobaciones el jurado no ha lanzado
ninguna de los anteriores veredictos el sistema va comparar las salidas que
produjo la solución y la comparará con las salidas de muestras que tiene para
los juegos de datos que se probarón en la solución y si ambas salidas son iden-
ticas en tonces el verdicto es Aceptado sino será Respuesta Incorrecta.
Pueden existir otro grupo de posibles veredictos que lancen los jurados
pero son propios ese sistema en sı́, los anteriormentes expuestos son los más
comunes. De igual forma existen jurados que te dan una evaluación general
de tu solución a partir de la evaluación de tu solución para cada caso probado
e incluso te dicen la evaluación para cada caso probado.
502
MISCELÁNEAS
Leer todos los problemas: Bajo ningún concepto usted como concur-
sante debe abandonar o terminar una competencia sin leer y entender
los problemas que su equipo intento resolver sin exito, y aquellos que ni
siquiera se penso en una solución. Muchos son los casos en que equipos
han salido o terminado una competencia y luego se han enterado que
hubo uno o un grupo de problemas que estaban a su alcance y sencilla-
mente no lo hicieron porque era un problema con un texto demasiado
largo. Tomesé los primeros 20-30 minutos de la competencia para leer
y entender todos los problemas, eso lo va ayudar a determinar el orden
y la distribución de los problemas en el equipo.
503
MISCELÁNEAS
504
MISCELÁNEAS
16.5. Equipo
A la hora de crear un equipo de tres estudiantes para los concurso ACM-
ICPC existen dos variantes:
505
MISCELÁNEAS
las de vacı́o, con lo cual garantizó que en cualquier transición variarı́a tan
sólo un bit.
En la actualidad, el código Gray se emplea como parte del algoritmo de
diseño de los mapas de Karnaugh, los cuales son, a su vez, utilizados como
”herramienta de diseño.en la implementación de circuitos combinacionales y
circuitos secuenciales. La vigencia del código Gray se debe a que un diseño
digital eficiente requerirá transiciones más simples y rápidas entre estados
lógicos (0 ó 1), por ello es que se persiste en su uso, a pesar de que los
problemas de ruido y potencia se hayan reducido con la tecnologı́a de estado
sólido de los circuitos integrados.
Utilizando el código Gray es posible también resolver el problema de
las Torres de Hanói. Se puede incluso formar un ciclo hamiltoniano o un
hipercubo, en el que cada bit se puede ver como una dimensión.
Debido a las propiedades de distancia de Hamming que posee el código
Gray, es usado en ocasiones en algoritmos genéticos.
Las computadoras antiguas indicaban posiciones abriendo y cerrando in-
terruptores. Utilizando tres interruptores como entradas usando Base 2, estas
dos posiciones estarı́an una después de la otra. El problema con el código bi-
nario en base 2 es que con interruptores mecánicos, es realmente difı́cil que
todos los interruptores cambien al mismo tiempo. En la transición de los dos
estados mostrados arriba, tres interruptores cambian de sitio. En el lapso
en el que los interruptores están cambiando, se pueden presentar salidas de
información espurias. Si las salidas mencionadas alimentan un circuito se-
cuencial, probablemente el sistema presentará un error en entrada de datos.
El código gray resuelve este problema cambiando solamente un dı́gito a la
vez, ası́ que no existe este problema. Tienes que tener en cuenta que para
convertir de binarios a Gray los valores que deben ser sumados en base 2 to-
man los siguientes valores 1+1=0, 0+0=0 , 1+0=1 y 0+1=1 esta operación
de forma vertical
Para convertir un número binario (en Base 2) a código Gray, simplemente
se le aplica una operación XOR con el mismo número desplazado un bit a la
derecha, sin tener en cuenta el acarreo.
Para relaizar la operación inversa es decir llevar de código gray a número
binario (Base 2) realizams el siguiente algoritmo:
Definimos un vector g conteniendo los dı́gitos en gray y otro vector b
destinado a contener los dı́gitos en Base 2
g0 es el dı́gito que se encuentra en el extremo izquierdo de la represen-
tación en código gray.
b0 es el dı́gito de mayor peso y que se encuentra en el extremo izquierdo
en la representación en Base 2.
506
MISCELÁNEAS
int r e v g ( int g )
{
int n = 0 ;
f o r ( ; g ; g>> = 1 )
n ˆ = g;
return n ;
}
16.7. Parser
16.7.1. Leer una lı́nea completa con C++
Uno de los problemas que se enfrentan los concursantes sobre todo los
que programan en C++ es el de leer toda una lı́nea de datos y despues
”dividir”dicha entrada en palabras. Para solcionar esto vamos en dividir este
dos momentos:
Captura de datos: Bien para leer una lı́nea completa con espacios
incluidos en C++ existen dos variantes de acuerdo a como se lea los
datos. Si usamos la función scanf serı́a de la siguiente manera:
507
MISCELÁNEAS
s c a n f ( " %[^\n]" , s ) ;
lee todos los caracteres que encuentra hasta que llega al carácter nueva
lı́nea ’
n’. Esta sentencia puede utilizarse por tanto para leer lı́neas completas,
con blancos incluidos.
Mientras en caso que utilizemos cin se puede utilizar lo siguiente:
string line
g e t l i n e ( cin , l i n e )
Hay que tener en cuenta que en ambos casos que el salto de lı́nea no lo
captura y si vuelves a leer otra cadena seguido solo vas a leer el salto
de lı́nea.
v e c t o r <s t r i n g > s p l i t A l l ( s t r i n g s )
{
stringstream ss ( s ) ;
v e c t o r <s t r i n g > v ;
while ( s s >> b )
{
v . push back ( b ) ;
}
return v ;
}
v e c t o r <s t r i n g > s p l i t A l l ( s t r i n g l i n e , s t r i n g
separator )
{
v e c t o r <s t r i n g > v ;
f o r ( int p =0;( p= l i n e . f i n d ( s e p a r a t o r ) ) !=
l i n e . npos ; )
{
v . push back ( l i n e . s u b s t r ( 0 , p ) ) ;
l i n e= l i n e . s u b s t r ( p + s e p a r a t o r .
size () ) ;
}
v . push back ( l i n e ) ;
return v ;
508
MISCELÁNEAS
En ambos algoritmo hay que tener en cuenta que puede existir ”palabras.o
”tokens”vacios o de longitud igual cero. Esto es bien fácil de solucionar,
solo basta con comprobar que la palabra tenga longitud distinto de cero
antes de adicionarla al vector resultante.
16.8. Puzzle 15
El puzzle 15 consiste en una matriz de 4x4 donde existen todos los núme-
ros del 1 al 15 con un espacio vacı́o. El objetivo del juego es colocar todos los
números del 1 al 15 desde de la esquina superior izquierda dejando el espacio
en la esquina inferior derecha tal y como se muestra en la figura.
509
MISCELÁNEAS
dada una configuración del tablero del puzzle 15 saber si puede llegar a la
configuración ganadora del juego.
Bueno vamos a tomar el tablero como un arreglo unidimensional de 16
elementos y a la configuración dada en el tablero es una de las 16! permuta-
ciones que pueden generar ese conjunto de elementos en el caso de la celda
vacı́a colocaremos el valor 0.
Llamaremos N al número de inversiones( cantidad de veces que se cumple
que: ai y aj son elementos del arreglo tal que i < j, pero ai > aj ) dentro del
arreglo unidimensional.
Ahora llamaremos K el indicé de la fila donde se ubica el valor cero en el
arreglo que representa la casilla vacı́a en la configuración inicial del tablero.
Se puede calcular como K = (z − 1) div 4 + 1 siendo z la posicion dentro del
arreglo unidimensional.
Luego si N + K es un número par podemos afirmar con certeza que a
partir de esa configuración dada se puede llegar a la configuración ganadora
del juego.
Luego la implementacion serı́a ası́:
int a [ 1 6 ] ;
f o r ( int i =0; i <16; ++i )
c i n >> a [ i ] ;
int i n v = 0 ;
f o r ( int i =0; i <16; ++i )
if (a [ i ])
f o r ( int j =0; j <i ; ++j )
if (a [ j ] > a [ i ])
++i n v ;
f o r ( int i =0; i <16; ++i )
i f ( a [ i ] == 0 )
i n v += 1 + i / 4 ;
16.9. Recursividad
La recursividad es una técnica de programación en la que un función,
método o subrutina hace una llamada a si mismo con el fin de resolver el
problema. La llamada a si mismo se conoce como llamada recursiva. Dicho
formalmente, un algoritmo se dice recursivo si calcula instancias de un pro-
blema en función de otras instancias del mismo problema hasta llegar a un
caso base, que suele ser una instancia pequeña del problema, cuya respuesta
510
MISCELÁNEAS
511
MISCELÁNEAS
2451 - Fifteen Puzzle El problema nos pide que dada una configuración
del tablero del juego Puzzle 15 determinar si se puede resolver o no. Entien-
dasé como resolver como realizar una secuencia de movimentos de forma que
se puede colocar todos los numeros en orden desde el 1 al 15 comenzando
desde la esquina superior izquierda dejando el espacio vació en la esquina
inferior derecha. Aplicando el algoritmo explicado en la sección Puzzle 15 de
esta capı́tulo se puede resolver.
512