CURSO ON-LINE DE PREPARACIÓN |
Sesión 5. Más problemas de grafos. |
Recordad que hay tres tareas principales en este curso: practicar, practicar y practicar, resolviendo cuantos más problemas mejor. Debes concienciarte de que la mejor y única forma de mejorar tus habilidades de resolución de problemas es... resolviendo problemas.
Con el fin de ofrecer más herramientas de práctica, en esta sesión vamos a presentar un nuevo juez on-line: el de la Olimpiada Informática Española. Dentro de este juez vamos a discutir un problema de grafos de complejidad media-alta, planteando diversas soluciones.
La OIE es un concurso de programación dirigido a todos los alumnos de bachillerato y secundaria de España. Dicho así, podría parecer que este concurso tiene un nivel insuficiente para nuestros propósitos. Sin embargo, debemos aclarar que el nivel de los problemas más complejos no tiene nada que envidiar a muchos de los problemas típicos de las olimpiadas universitarias. Así que, teniendo este recurso, ¿por qué desaprovecharlo?
Para acceder a los problemas del juez de la OIE es necesario registrarse en:
http://olimpiada-informatica.org/De forma periódica se organizan concursos on-line de preparación, en los que van apareciendo problemas nuevos. Los problemas están clasificados en diferentes categorías: introductorios, simulación, voraces, numéricos, etc. La colección de programación dinámica es especialmente completa e interesante. Merece la pena echar un vistazo a todas las categorías.
Una característica específica de la OIE (y también de la Olimpida Internacional Informática (IOI)), en relación a los concursos universitarios, es el hecho de que en cada problema podemos obtener entre 0 y 100 puntos. Los problemas tienen diferentes casos de prueba, en orden creciente de dificultad. Cuantos más casos de prueba pueda resolver nuestro programa, es decir, cuanto mejor sea nuestra solución, más puntos podemos obtener de ese problema.
Normalmente esta dificultad de los problemas está relacionada con el tamaño de los casos de prueba. Por ejemplo, en un problema de grafos hipotético, podríamos obtener hasta 40 puntos si resolvemos el problema con grafos de 10 nodos; 80 puntos si lo resolvemos con grafos de 1000 nodos; y 100 puntos si lo resolvemos con grafos de 10000 nodos. El tipo de algoritmos que pueden servir para resolver el problema para 10 nodos, puede ser completamente ineficiente para 1000 nodos o más.
Una vez presentado el juez de la OIE, vamos a trabajar con un problema del mismo. Se trata de un problema de grafos, que apareció en el quinto concurso on-line de preparación:
PROBLEMA: Warp-world : Enunciado del problema - Enlace al juez (recuerda registrarte en este juez)
Se trata de una nueva variante de los problemas de tableros y laberintos. Tenemos un tablero que representa un mapa de posiciones. El objetivo es encontrar el camino más corto entre dos posiciones (la "(1)" y la "(2)"). Podemos movernos arriba, abajo, a la izquierda o a la derecha. La particularidad es que algunas casillas (las que tienen las mismas letras) son equivalentes entre sí, por lo que podemos movernos a cualquiera de ellas de manera instantátena.
Ejemplo: Un
tablero del problema y un recorrido sobre el mismo. Los warps-points (las casillas que tienen letras) son
posiciones equivalentes entre sí; podemos movernos instantáneamente
entre los que tienen la misma letra. También podemos pasar por encima de ellos sin cambiar de
posición. En este ejemplo, la distancia mínima entre (1) y (2) es de 4.
Se puede ver muy fácilmente que se trata de un problema de caminos mínimos. Los nodos del grafo son cada una de las casillas del tablero; las aristas son las cuatro posiciones adyacentes a cada casilla, más las aristas correspondientes a los warp-points (que serían aristas de coste 0 entre todos los warps-points con la misma letra).
Una vez construido el grafo, podemos calcular el camino mínimo entre (1) y (2) usando el algoritmo de Dijkstra. Equivalentemente, recordemos que cuando las aristas de un grafo son de coste 1, se pueden calcular los caminos mínimos usando la búsqueda primero en anchura partiendo del nodo origen. Por lo tanto, un algoritmo sencillo para obtener la solución del problema podría ser el siguiente. En adelante, suponemos que la posición (1) es (x1, y1), y la posición 2 es (x2, y2):
1. Crear una matriz D, del mismo tamaño que el tablero, para calcular los costes de los caminos. Inicializarla a INFINITO (un valor muy grande), para indicar que todavía no hemos calculado la solución.
2. Crear una cola de posiciones, C (es decir, una cola donde cada elemento es un par (x, y)), metiendo inicialmente (x1, y1) en la cola. Inicializar también D[x1, y1] a valor 0.
3. Repetir:
3.1. Sacar el tope de la cola. Supongamos que es
(x, y).
3.2. Si el tope es igual a (x2, y2), entonces
acabamos. Ya sabemos su coste mínimo en D[x2, y2].
3.3. En otro caso, para cada una de las cuatro
casillas adyacentes, (xa, ya), de (x, y) hacer:
3.3.1. Si D[x, y]+1 es menor que D[xa,
ya] entonces:
3.3.1.1.
Actualizar D[xa, ya] con D[x1, y1]+1. Meter en la cola (xa, ya).
3.3.1.2. Si (xa,
ya) es un wrap-point,
actualizar todas las casillas equivalentes (xe, ye) (es decir, las que
tengan la misma letra que (xa, ya)) con el mismo valor que D[xa, ya] y
meterlas también todas en la cola. Para optimizar este paso, podemos tener
almacenado en un array de 26 posiciones, para cada letra, todas las
casillas que tengan esa letra.
Observar que estamos haciendo un algoritmo de búsqueda primero en anchura sobre un grafo, aunque sin construir explícitamente el grafo (en el peor caso, con 400 filas y columnas, el número de nodos del grafo sería de 160.000).
El anterior algoritmo es capaz de resolver el problema planteado. Pero, ¿será suficientemente rápido? La eficiencia de la búsqueda primero en anchura, sin construir explícitamente el grafo, sería de O(n+a), siendo n el número de nodos y a el número de aristas. Como en este problema a se puede considerar proporcional a n, podemos decir que el tiempo es un O(n) para cada consulta.
En el problema se nos pide resolver k preguntas por cada tablero, por lo que el tiempo total sería de O(k*n). ¿Es eso suficientemente rápido?
Como n corresponde a: filas * columnas, en el primer caso (50 filas y columnas y 10 preguntas), el número de operaciones sería proporcional a: 50*50*10 = 25.000. ¡Perfecto!En el segundo caso (100 filas y columnas y 100 preguntas), sería proporcional a: 100*100*100 = 1.000.000. No hay problema.
Pasemos al tercer caso (200 filas y columnas y 1.000 preguntas), tenemos: 200*200*1000 = 40.000.000. Vaya, ya está un poco más justo...
En el cuarto caso (400 filas y columnas y 10.000 preguntas), tenemos: 1.600.000.000. ¡Imposible!
No nos queda más remedio que buscar un método más eficiente para resolver el problema. Con casos que contienen 10.000 preguntas (es decir, calcular 10.000 caminos mínimos) parece deducirse que necesitamos algún método capaz de resolver cada consulta en un tiempo constante.
Vamos a partir de la siguiente idea: todos los warp-points con la misma letra pueden considerarse realmente como un solo nodo. Por lo tanto, tenemos los 26 posibles nodos de los warp-points (uno por cada letra), más las posiciones sin letras. El camino mínimo de (1) a (2) puede pasar por los warp-points o no; así que debemos calcular caminos mínimos entre los warp-points.
El algoritmo sería como el siguiente:
Obviamente, hemos complicado un poco la resolución del problema. Pero, en cambio, hemos conseguido un método más eficiente, puesto que se precalculan muchos costes de caminos mínimos.
¿Será suficientemente rápido el nuevo método? Vamos a analizar cada uno de los pasos anteriores:
Por lo tanto, el orden de complejidad total sería: O(2*26n + 26*26*k). Considerando el caso más grande (400 filas y columnas y 10.000 preguntas), tenemos que el número de operaciones sería proporcional a: 2*26*400*400 + 26*26*10.000 = 8.320.000 + 6.760.000 = 15.080.000 operaciones. Lo cual es más de 100 veces más rápido que el método sencillo. Con una implementación suficientemente cuidada, el algoritmo entraría perfectamente en tiempo. Como ejercicio, dejamos a los alumnos la implementación de este método.
El número de problemas interesantes que podemos encontrar en el juez on-line de la OIE es muy grande. En esta sesión no vamos a proponer un problema concreto, sino que dejamos a elección entre todos los problemas de grafos del juez de la OIE. Recordamos que los problemas están clasificados con medallas de bronce, plata, oro y copa, en nivel creciente de dificultad. Como ya dijimos, para que los enlaces funcionen es necesario registrarse en dicho juez.
Harrichu y el laberinto (OIE08/p1) | 4harrichu | ||
The Voight-Kampff test | voightkampff | ||
Velocirráptors 101 (OIE08/F1) | velo101 | ||
Papa Noel y su trineo (OIE09/p3) | trineo-noel | ||
Los regalos de Papa Noel (2) (OIE09/p3) | regalos-noel2 | ||
Amigos (OIE09/c2) | amigos | ||
Tratado de paz 2 (OIE09/p11) | tratado-paz2 | ||
Harrichu y la competición ciclista (OIE09/p13) | harrichuciclista | ||
El de Indiana Jones (OIE08/F2) | indiana | ||
Más amigos (OIE10/p9) | masamigos | ||
El burro de ajedrez (OIE10/p13) | donkey-chess | ||
Derrame de petróleo (OIE10/p13) | oil-spill-1 | ||
Derrame de petróleo (2) (OIE10/p13) | oil-spill-2 |
Olimpiada de programación- Facultad de Informática - Universidad de Murcia