Sei sulla pagina 1di 45

21/10/2016 Algorithms | Algorithms and More

Algorithms and More


Blog about programming!

ARCHIVO DE LA CATEGORÍA: ALGORITHMS

ÁRBOL DE EXPANSIÓN MÍNIMA: ALGORITMO DE KRUSKAL


Publicado el abril 19, 2012| 20 comentarios

Antes de explicar directamente el algoritmo de Kruskal, comenzaré dando conceptos sobre que es un árbol de expansión
mínima para entender mejor el problema.

Árbol de Expansión

Dado un grafo conexo, no dirigido G. Un árbol de expansión es un árbol compuesto por todos los vértices y algunas
(posiblemente todas) de las aristas de G. Al ser creado un árbol no existirán ciclos, además debe existir una ruta entre cada
par de vértices.

Un grafo puede tener muchos arboles de expansión, veamos un ejemplo con  el siguiente grafo:

En la imagen anterior se puede observar que el grafo dado posee 3 arboles de expansión, dichos arboles cumplen con las
propiedades antes mencionadas como son unir todos los vértices usando algunas aristas.

Árbol de Expansión Mínima

Dado un grafo conexo, no dirigido y con pesos en las aristas, un árbol de expansión mínima es un árbol compuesto por todos
los vértices y cuya suma de sus aristas es la de menor peso. Al ejemplo anterior le agregamos pesos a sus aristas y obtenemos
los arboles de expansiones siguientes:

https://jariasf.wordpress.com/category/algorithms/ 1/45
21/10/2016 Algorithms | Algorithms and More

De la imagen anterior el árbol de expansión mínima seria el primer árbol de expansión cuyo peso total es 6.

El problema de hallar el Árbol de Expansión Mínima (MST) puede ser resuelto con varios algoritmos, los mas conocidos con
Prim y Kruskal ambos usan técnicas voraces (greedy).

Algoritmo de Kruskal

Para poder comprender el algoritmo de kruskal será necesario revisar primer el tutorial de Union-Find.

Como trabaja:

Primeramente ordenaremos las aristas del grafo por su peso de menor a mayor. Mediante la técnica greedy Kruskal intentara
unir cada arista siempre y cuando no se forme un ciclo, ello se realizará mediante Union-Find. Como hemos ordenado las
aristas por peso comenzaremos con la arista de menor peso, si los
vértices que contienen dicha arista no están en la misma componente
conexa  entonces los unimos para formar una sola componente
mediante Union(x , y), para revisar si están o no en la misma
componente conexa usamos la función SameComponent(x , y) al hacer
esto estamos evitando que se creen ciclos y que la arista que une dos
vértices siempre sea la mínima posible.

Algoritmo en Pseudocódigo

1  método Kruskal(Grafo):
2      inicializamos MST como vacío
3      inicializamos estructura unión-find
4      ordenamos las aristas del grafo por peso de menor a mayor.
5      para cada arista e que une los vértices u y v
6          si u y v no están en la misma componente
7             agregamos la arista e al MST     
8             realizamos la unión de las componentes de u y v

https://jariasf.wordpress.com/category/algorithms/ 2/45
21/10/2016 Algorithms | Algorithms and More
Ejemplo y código paso a paso

Tengamos el siguiente grafo no dirigido:

Como podemos ver en la imagen anterior la definición de nuestro grafo en código sería:

1 struct Edge{
2     int origen;     //Vértice origen
3     int destino;    //Vértice destino
4     int peso;       //Peso entre el vértice origen y destino
5     Edge(){}
6     …
7 }arista[ MAX ];      //Arreglo de aristas para el uso en kruskal

Primeramente usaremos el método MakeSet de unión-find para inicializar cada componente, obteniendo las siguientes
componentes conexas iniciales:

Ahora el siguiente paso es ordenar las aristas del grafo en orden ascendente:

https://jariasf.wordpress.com/category/algorithms/ 3/45
21/10/2016 Algorithms | Algorithms and More

Para el ordenamiento podemos usar las librerías predefinidas de Java y C++ como estamos ordenando estructuras
necesitamos un comparador, en este caso estamos ordenando por peso por lo tanto dentro de la estructura antes definida
agregamos:

1 struct Edge{
2     …
3     //Comparador por peso, me servira al momento de ordenar lo realizara en orden ascendente
4     //Cambiar signo a > para obtener el arbol de expansion maxima
5     bool operator<( const Edge &e ) const {
6         return peso < e.peso;
7     }
8 }arista[ MAX ];      //Arreglo de aristas para el uso en kruskal

Ordenamos el arreglo de aristas mediante lo siguiente:

1 std::sort( arista , arista + E );    //Ordenamos las aristas por su comparador

Lo siguiente será recorrer todas las aristas ya ordenadas y verificar si sus vértices están o no en la misma componente.

La primera arista a verificar es la que une a los vértices 8 y 7, verificamos si están en la misma componente, para ello
hacemos Find(8) , Find(7):

https://jariasf.wordpress.com/category/algorithms/ 4/45
21/10/2016 Algorithms | Algorithms and More
Como podemos observar en la tabla y en la misma imagen no están en la misma componente conexa, por tanto esta arista es
valida para el MST así que unimos los vértices por el método de Union( 8 , 7 ).

Continuamos con la siguiente arista:

Observamos la tabla de Union-Find y vemos que Find(3) != Find(9). Entonces es posible realizar la unión de ambas
componentes:

Continuamos con la siguiente arista:

https://jariasf.wordpress.com/category/algorithms/ 5/45
21/10/2016 Algorithms | Algorithms and More

En la imagen podemos observar que ambos vértices no están en la misma componente, por tanto realizamos la Union( 6 , 7 ):

Continuamos con la siguiente arista, los vértices 1 y 2 no están en la misma componente conexa:

Realizamos la Union(1,2):

https://jariasf.wordpress.com/category/algorithms/ 6/45
21/10/2016 Algorithms | Algorithms and More
Continuamos con la siguiente arista:

Al observar la imagen los vértices 3 y 6 están en distinta componentes conexas. Entonces realizamos la Union(3,6) y
actualizamos la tabla de Union-Find.

Continuamos con la siguiente arista:

En este caso si observamos la imagen los vértices 7 y 9 están en la misma componente conexa; asimismo en la tabla de
Union-Find el elemento raíz del vértice 7 es el mismo que el del vértice 9 por ello afirmamos que están el a misma
componente conexa, por lo tanto no habrá que realizar la unión de ambos vértices. Con esto evitamos tener ciclos en el árbol
de expansión mínima.

Continuamos con la siguiente arista:

https://jariasf.wordpress.com/category/algorithms/ 7/45
21/10/2016 Algorithms | Algorithms and More

Al observar la imagen los vértices 3 y 4 no están en la misma componente conexa por lo tanto realizamos la Union(3,4) en el
grafo:

Continuamos con la siguiente arista:

Los vértices 8 y 9 están en la misma componente conexa por lo tanto no realizamos Unión de vértices. Continuemos con la
siguiente arista:

Los vértices 1 y 8 están diferentes componentes. Realizamos la Union(1,8) en el grafo:

https://jariasf.wordpress.com/category/algorithms/ 8/45
21/10/2016 Algorithms | Algorithms and More

Continuamos con la siguiente arista:

Los vértices 2 y 3 están en la misma componente conexa por lo tanto no realizamos Union de componentes. Continuamos con
la siguiente arista:

Los vértices 4 y 7 no están en la misma componente conexa, realizamos Union(4,5) en el grafo:

https://jariasf.wordpress.com/category/algorithms/ 9/45
21/10/2016 Algorithms | Algorithms and More

Como podemos observar ya están todos los vértices del grafo conectados así que al momento de continuar viendo las demás
aristas ordenadas siempre tendremos e l caso de que ya están en la misma componente conexa por lo tanto el Árbol de
Expansión Mínima para el grafo es el siguiente:

El peso total del árbol de expansión mínima para el grafo mostrado es 39.

En código simplemente es iterar sobre el arreglo de aristas ingresado y ordenado obteniendo sus respectivos datos. Para
verificar si están o no en la misma componente usamos el método sameComponent explicado en el tutorial de Union-Find:

1 for( int i = 0 ; i < E ; ++i ){     //Recorremos las aristas ya ordenadas por peso
2     origen = arista[ i ].origen;    //Vértice origen de la arista actual
3     destino = arista[ i ].destino;  //Vértice destino de la arista actual
4     peso = arista[ i ].peso;        //Peso de la arista actual
5     //Verificamos si estan o no en la misma componente conexa
6     if( !sameComponent( origen , destino ) ){  //Evito ciclos
7         total += peso;              //Incremento el peso total del MST
8         MST[ numAristas++ ] = arista[ i ];  //Agrego al MST la arista actual
9         Union( origen , destino );  //Union de ambas componentes en una sola
10     }
11 }

Verificación de MST

Para que sea un MST válido el número de aristas debe ser igual al número de vértices – 1. Esto se cumple debido a que el
MST debe poseer todos los vértices del grafo ingresado y además no deben existir ciclos. Si vemos el ejemplo antes explicado
tenemos en el MST:

Número de Aristas = 8
Número de Vértices = 9

https://jariasf.wordpress.com/category/algorithms/ 10/45
21/10/2016 Algorithms | Algorithms and More
Cumple con lo dicho -> 9 – 1 = 8 por tanto tenemos un MST válido. Veamos otro ejemplo teniendo como grafo ingresado lo
siguiente:

Como podemos observar el grafo ingresado posee 2 componentes conexas, al aplicar kruskal obtendremos los siguientes
MST:

En la imagen podemos observar el MST luego de aplicar kruskal, sin embargo no es un MST válido porque no tiene 1
componente conexa que posea todos los vértices, comprobemos:

Número de Aristas = 7
Número de Vértices = 9

No cumple lo dicho inicialmente 9 – 1 != 7 por lo tanto tenemos un MST invalido. En código basta con un if:

1 //Si el MST encontrado no posee todos los vértices mostramos mensaje de error
2 //Para saber si contiene o no todos los vértices basta con que el numero
3 //de aristas sea igual al numero de vertices ‐ 1
4 if( V ‐ 1 != numAristas ){
5     puts("No existe MST valido para el grafo ingresado, el grafo debe ser conexo.");
6     return;
7 }

Problemas de diferentes Jueces

UVA

908 – Re-connecting Computer Sites

1208 – Oreon

10034 – Freckles

https://jariasf.wordpress.com/category/algorithms/ 11/45
21/10/2016 Algorithms | Algorithms and More
10462 – Is There A Second Way Left?

10600 – ACM contest and Blackout

10842 – Traffic Flow

11228 – Transportation System

11631 – Dark roads

11710 – Expensive subway

11733 – Airports

11747 – Heavy Cycle Edges

11857 – Driving Range

COJ

1016 – Freckles

1690 – Bad Cowtractors

TOPCODER

SRM 356 DIV2-1000 – RoadReconstruction

SRM 492 DIV2 – 1000 – TimeTravellingSalesman

HDU

1102 – Constructing Roads

POJ

2377 – Bad Cowtractors

2421 – Constructing Roads

TJU

2531 – Oreon

Códigos:

Implementación del algoritmo en C++: Algoritmo de Kruskal

Implementación del algoritmo en JAVA: Algoritmo de Kruskal

https://jariasf.wordpress.com/category/algorithms/ 12/45
21/10/2016 Algorithms | Algorithms and More
Por Jhosimar George Arias Figueroa

Publicado en Algorithms, Main 20 comentarios


Etiquetado graph, kruskal, minimum spanning tree, mst, union find

CAMINO MAS CORTO: ALGORITMO DE DIJKSTRA


Publicado el marzo 19, 2012| 44 comentarios

Para el problema de la ruta corta tenemos varios algoritmos, en esta oportunidad se explicará el algoritmo de dijkstra el cual
usa una técnica voraz (greedy). Al final del articulo se encuentran adjuntas las implementaciones en C++ y JAVA.

Descripción

El algoritmo de dijkstra determina la ruta más corta desde un nodo origen hacia los demás nodos para ello es requerido como
entrada un grafo cuyas aristas posean pesos. Algunas consideraciones:

Si los pesos de mis aristas son de valor 1, entonces bastará con usar el algoritmo de BFS.
Si los pesos de mis aristas son negativos no puedo usar el algoritmo de dijsktra, para pesos negativos tenemos otro
algoritmo llamado Algoritmo de Bellmand-Ford.

Como trabaja

Primero marcamos todos los vértices como no utilizados. El algoritmo parte de un vértice origen que será ingresado, a partir
de ese vértices evaluaremos sus adyacentes, como dijkstra usa una técnica greedy – La técnica greedy utiliza el principio de
que para que un camino sea óptimo, todos los caminos que contiene también
deben ser óptimos­ entre todos los vértices adyacentes, buscamos el que esté
más cerca de nuestro punto origen, lo tomamos como punto intermedio y
vemos si podemos llegar más rápido a través de este vértice a los demás.
Después escogemos al siguiente más cercano (con las distancias ya
actualizadas) y repetimos el proceso. Esto lo hacemos hasta que el vértice no
utilizado más cercano sea nuestro destino. Al proceso de actualizar las
distancias tomando como punto intermedio al nuevo vértice se le conoce
como relajación (relaxation).

Dijkstra es muy similar a BFS, si recordamos BFS usaba una Cola para el
recorrido para el caso de Dijkstra usaremos una Cola de Prioridad o Heap, este Heap debe tener la propiedad de Min-Heap es
decir cada vez que extraiga un elemento del Heap me debe devolver el de menor valor, en nuestro caso dicho valor será el
peso acumulado en los nodos.

Tanto java como C++ cuentan con una cola de prioridad ambos implementan un Binary Heap aunque con un Fibonacci Heap
la complejidad de dijkstra se reduce haciéndolo mas eficiente, pero en un concurso mas vale usar la librería que intentar
programar una nueva estructura como un Fibonacci Heap, claro que particularmente uno puede investigar y programarlo
para saber como funciona internamente.

Algoritmo en pseudocódigo

https://jariasf.wordpress.com/category/algorithms/ 13/45
21/10/2016 Algorithms | Algorithms and More
Considerar distancia[ i ] como la distancia mas corta del vértice origen ingresado al vértice i.

1  método Dijkstra(Grafo,origen):
2      creamos una cola de prioridad Q
3      agregamos origen a la cola de prioridad Q
4      mientras Q no este vacío:
5          sacamos un elemento de la cola Q llamado u
6          si u ya fue visitado continuo sacando elementos de Q    
7          marcamos como visitado u
8          para cada vértice v adyacente a u en el Grafo:
9              sea w el peso entre vértices ( u , v )  
10             si v no ah sido visitado:
11                Relajacion( u , v , w )

1  método Relajacion( actual , adyacente , peso ):


2      si distancia[ actual ] + peso < distancia[ adyacente ]
3         distancia[ adyacente ] = distancia[ actual ] + peso
4         agregamos adyacente a la cola de prioridad Q

Ejemplo y Código paso a paso

Tengamos el siguiente grafo, cuyos ID están en color negro encima de cada vértice, los pesos esta en color azul y la distancia
inicial en cada vértice es infinito

Algunas consideraciones para entender el código que se explicara junto con el funcionamiento del algoritmo.

1 #define MAX 10005 //maximo numero de vértices
2 #define Node pair< int , int > //definimos el nodo como un par( first , second ) donde first es el verti
3 #define INF 1<<30 //definimos un valor grande que represente la distancia infinita inicial, basta conque

Inicializamos los valores de nuestros arreglos

https://jariasf.wordpress.com/category/algorithms/ 14/45
21/10/2016 Algorithms | Algorithms and More

Donde:

Vértices: ID de los vértices.


Distancia[ u ]: Distancia mas corta desde vértice inicial a vértice con ID = u.
Visitado[ u ]: 0 si el vértice con ID = u no fue visitado y 1 si ya fue visitado.
Previo[ u ]: Almacenara el ID del vértice anterior al vértice con ID = u, me servirá para impresión del camino mas
corto.

En cuanto al código los declaramos de la siguiente manera:

1 vector< Node > ady[ MAX ]; //lista de adyacencia
2 int distancia[ MAX ];      //distancia[ u ] distancia de vértice inicial a vértice con ID = u
3 bool visitado[ MAX ];      //para vértices visitados
4 int previo[ MAX ];         //para la impresion de caminos
5 priority_queue< Node , vector<Node> , cmp > Q; //priority queue propia del c++, usamos el comparador def
6 int V;                     //numero de vertices

Y la función de inicialización será simplemente lo siguiente

1 //función de inicialización
2 void init(){
3     for( int i = 0 ; i <= V ; ++i ){
4         distancia[ i ] = INF;  //inicializamos todas las distancias con valor infinito
5         visitado[ i ] = false; //inicializamos todos los vértices como no visitado
6         previo[ i ] = ‐1;      //inicializamos el previo del vértice i con ‐1
7     }
8 }

De acuerdo al vértice inicial que elijamos cambiara la distancia inicial, por ejemplo la ruta más corta partiendo del vértice 1 a
todos los demás vértices:

El vértice 1 es visitado, la distancia de vértice 1 -> vértice 1 es 0 por estar en el mismo lugar.

1 Q.push( Node( inicial , 0 ) ); //Insertamos el vértice inicial en la Cola de Prioridad
2 distancia[ inicial ] = 0;      //Este paso es importante, inicializamos la distancia del inicial como 0

Extraemos el tope de la cola de prioridad

1 while( !Q.empty() ){                   //Mientras cola no este vacia
2         actual = Q.top().first;            //Obtengo de la cola el nodo con menor peso, en un comienzo s
3         Q.pop();                           //Sacamos el elemento de la cola
https://jariasf.wordpress.com/category/algorithms/ 15/45
21/10/2016 Algorithms | Algorithms and More
Si el tope ya fue visitado entonces no  tengo necesidad de evaluarlo, por ello continuaría extrayendo elementos dela cola:

1 if( visitado[ actual ] ) continue; //Si el vértice actual ya fue visitado entonces sigo sacando elemento

En este caso al ser el tope el inicial no esta visitado por lo tanto marcamos como visitado.

1 visitado[ actual ] = true;         //Marco como visitado el vértice actual

Hasta este momento la tabla quedaría de la siguiente manera

Ahora vemos sus adyacentes que no hayan sido visitados. Tendríamos 2 y 4.

1 for( int i = 0 ; i < ady[ actual ].size() ; ++i ){ //reviso sus adyacentes del vertice actual
2     adyacente = ady[ actual ][ i ].first;   //id del vertice adyacente
3     peso = ady[ actual ][ i ].second;        //peso de la arista que une actual con adyacente ( actual ,
4     if( !visitado[ adyacente ] ){        //si el vertice adyacente no fue visitado

Evaluamos primero para vértice 2

Vemos que la distancia actual desde el vértice inicial a 2 es ∞, verifiquemos el paso de relajación:

distancia[ 1 ] + 7 < distancia[ 2 ]      ­>       0 + 7 < ∞        ­>         7 < ∞

El paso de relajación es posible realizarlo entonces actualizamos la distancia en el vértice 2 y agregando el vértice en la cola
de prioridad con nueva distancia quedando:

https://jariasf.wordpress.com/category/algorithms/ 16/45
21/10/2016 Algorithms | Algorithms and More

El paso de relajación se daría en la siguiente parte:

1 for( int i = 0 ; i < ady[ actual ].size() ; ++i ){ //reviso sus adyacentes del vertice actual
2     adyacente = ady[ actual ][ i ].first;   //id del vertice adyacente
3     peso = ady[ actual ][ i ].second;        //peso de la arista que une actual con adyacente ( actual ,
4     if( !visitado[ adyacente ] ){        //si el vertice adyacente no fue visitado
5         relajacion( actual , adyacente , peso ); //realizamos el paso de relajacion
6     }
7 }

Donde la función de relajación seria

1 //Paso de relajacion
2 void relajacion( int actual , int adyacente , int peso ){
3     //Si la distancia del origen al vertice actual + peso de su arista es menor a la distancia del orige
4     if( distancia[ actual ] + peso < distancia[ adyacente ] ){
5         distancia[ adyacente ] = distancia[ actual ] + peso;  //relajamos el vertice actualizando la dis
6         previo[ adyacente ] = actual;                         //a su vez actualizamos el vertice previo
7         Q.push( Node( adyacente , distancia[ adyacente ] ) ); //agregamos adyacente a la cola de priorid
8     }
9 }

Ahora evaluamos al siguiente adyacente que es el vértice 4:

De manera similar al anterior vemos que la distancia actual desde el vértice inicial a 4 es ∞, verifiquemos el paso de
relajación:

distancia[ 1 ] + 2 < distancia[ 4 ]      ­>      0 + 2 < ∞        ­>        2 < ∞

https://jariasf.wordpress.com/category/algorithms/ 17/45
21/10/2016 Algorithms | Algorithms and More
El paso de relajación es posible realizarlo entonces actualizamos la distancia en el vértice 4 quedando:

En cuanto a la cola de prioridad como tenemos un vértice con menor peso este nuevo vértice ira en el tope de la cola:

Revisamos sus adyacentes no visitados que serian vértices 2, 3 y 5.

Empecemos por el vértice 2:

Ahora vemos que la distancia actual del vértice inicial al vértice 2 es 7, verifiquemos el paso de relajación:

distancia[ 4 ] + 3 < distancia[ 2 ]      ­>      2 + 3 < 7         ­>         5 < 7

En esta oportunidad hemos encontrado una ruta mas corta partiendo desde el vértice inicial al vértice 2, actualizamos la
distancia en el vértice 2 y actualizamos el vértice previo al actual quedando:

https://jariasf.wordpress.com/category/algorithms/ 18/45
21/10/2016 Algorithms | Algorithms and More

En cuanto a la cola de prioridad como tenemos un vértice con menor peso este nuevo vértice ira en el tope de la cola,
podemos ver que tenemos 2 veces el mismo vértice pero como usamos una técnica greedy siempre usaremos el valor óptimo:

Continuamos con los Vértices 3 y 5 como tienen valor ∞ si será posible relajarlos por lo que sería similar a los pasos iniciales
solo que en los pasos iniciales distancia[ 1 ] era 0 en este caso distancia[ 4 ] es 2, quedando:

Hemos terminado de evaluar al vértice 4, continuamos con el tope de la cola que es vértice 2, el cual marcamos como
visitado.

https://jariasf.wordpress.com/category/algorithms/ 19/45
21/10/2016 Algorithms | Algorithms and More

Los adyacentes no visitados del vértice 2 son los vértices 3 y 5. Comencemos con el vértice 3

Ahora vemos que la distancia actual del vértice inicial al vértice 3 es 10, verifiquemos el paso de relajación:

distancia[ 2 ] + 1 < distancia[ 3 ]      ­>      5 + 1 < 10         ­>         6 < 10

En esta oportunidad hemos encontrado una ruta mas corta partiendo desde el vértice inicial al vértice 3, dicha ruta sería 1 ->
4 -> 2 -> 3 cuyo peso es 6 que es mucho menor que la ruta 1 – > 4 -> 3 cuyo peso es 10, actualizamos la distancia en el vértice
3 quedando:

https://jariasf.wordpress.com/category/algorithms/ 20/45
21/10/2016 Algorithms | Algorithms and More

El siguiente vértice de la cola de prioridad es el vértice 3 y su único adyacente no visitado es el vértice 5.

Vemos que la distancia actual del vértice inicial al vértice 5 es 7, verifiquemos el paso de relajación:

distancia[ 3 ] + 5 < distancia[ 5 ]      ­>      6 + 5 < 7         ­>         11 < 7

En esta oportunidad se no cumple por lo que no relajamos el vértice 5, por lo que la tabla en cuanto a distancias no sufre
modificaciones y no agregamos vértices a la cola:

Ahora tocaría el vértice 2 pero como ya fue visitado seguimos extrayendo elementos de la cola, el siguiente vértice será el 5.

Al ser el ultimo vértice a evaluar no posee adyacentes sin visitar por lo tanto hemos terminado el algoritmo. En el grafico
anterior observamos que 2 aristas no fueron usadas para la relajación, las demás si fueron usadas. La tabla final quedaría de
https://jariasf.wordpress.com/category/algorithms/ 21/45
21/10/2016 Algorithms | Algorithms and More
la siguiente manera:

De la tabla si deseo saber la distancia mas corta del vértice 1 al vértice 5, solo tengo que acceder al valor del arreglo en su
índice respectivo (distancia[ 5 ]).

Shortest Path Tree

En el proceso anterior usábamos el arreglo previo[ u ] para almacenar el ID del vértice previo al vértice con ID = u, ello me
sirve para formar el árbol de la ruta mas corta y además me sirve para imprimir caminos de la ruta mas corta.

Impresión del camino encontrado.

Para imprimir el camino mas corto deseado usamos el arreglo previo[ u ], donde u tendrá el ID del vértice destino, o sea si
quiero imprimir el camino mas corto desde vértice 1 -> vértice 3 partiré desde previo[ 3 ] hasta el previo[ 1 ]. De manera
similar a lo que se explico en el algoritmo BFS, en este caso se realizara de manera recursiva:

1 //Impresion del camino mas corto desde el vertice inicial y final ingresados
2 void print( int destino ){
3     if( previo[ destino ] != ‐1 )    //si aun poseo un vertice previo
4         print( previo[ destino ] );  //recursivamente sigo explorando
5     printf("%d " , destino );        //terminada la recursion imprimo los vertices recorridos
6 }

Veamos gráficamente el funcionamiento, desde el grafo comenzamos en 3

https://jariasf.wordpress.com/category/algorithms/ 22/45
21/10/2016 Algorithms | Algorithms and More

El previo de 3 es el vértice 2, por lo tanto ahora evaluó 2:

Ahora el previo de 2 es el vértice 4:

https://jariasf.wordpress.com/category/algorithms/ 23/45
21/10/2016 Algorithms | Algorithms and More

El previo de 4 es el vértice inicial 1

Como el previo de 1 es -1 terminamos el recorrido, ahora en el retorno de las llamadas recursivas imprimo el camino: 1 4 2 3

Problemas de diferentes Jueces

UVA

341 – Non-Stop Travel

423 – MPI Maelstrom

https://jariasf.wordpress.com/category/algorithms/ 24/45
21/10/2016 Algorithms | Algorithms and More
10278 – Fire Station

10801 – Lift Hopping

12144 – Almost Shortest Path

TJU

1325 – Fire Station

SPOJ

15 – The Shortest Path (SHPATH)

50 – Invitation Cards (INCARDS)

440 – The Turtle´s Shortest Path (TSHPATH)

3405 – Almost Shortest Path (SAMER08A)

POJ

1511 – Invitation Cards

HDU

1535 – Invitation Cards

ICPC

4210 – Almost Shortest Path

COJ

1659 – Checking an Alibi

Códigos:

Implementación del algoritmo en C++: Algoritmo de Dijkstra

Implementación del algoritmo en JAVA: Algoritmo de Dijkstra

Por Jhosimar George Arias Figueroa

Publicado en Algorithms, Main 44 comentarios


Etiquetado dijsktra, graph, shortest path, sssp

https://jariasf.wordpress.com/category/algorithms/ 25/45
21/10/2016 Algorithms | Algorithms and More

ALGORITMO DE BÚSQUEDA: DEPTH FIRST SEARCH (PARTE 1)


Publicado el marzo 2, 2012| 10 comentarios

El algoritmo de búsqueda que se explicará a continuación es Depth First Search ( DFS ) se explicará el algoritmo de manera
similar a como se hizo BFS, proponiendo problemas y otorgando códigos del algoritmo en si.

Descripción

El algoritmo DFS posee varias aplicaciones la mas importante es para problemas de conectividad,  si un grafo es conexo,
detectar ciclos en un grafo,  numero de componentes conexas,  etc y es bastante útil en otro algoritmos como para hallar las
componentes fuertemente conexas en un grafo ( Algoritmo de Kosaraju, Algoritmo de Tarjan), para hallar puntos de
articulación o componentes biconexas ( puentes ),  para recorrido en un circuito o camino euleriano, topological sort, flood
fill y otras aplicaciones.

Como trabaja

DFS va formando un árbol al igual que BFS pero lo hace a


profundidad. Existen dos formas de hacer el recorrido una es
usando una Pila y otra de manera recursiva:

Usando Stack

El concepto es el mismo que BFS solo que se cambia la Cola por


una Pila, el proceso es como sigue: visitar el nodo inicial y
ponerlo en la pila, ahora para ver los siguientes nodos a visitar
sacamos el nodo tope de la pila y vemos sus adyacentes, los que
no han sido visitados los insertamos en la pila. El proceso se
repite hasta que la pila se encuentre vacía ( se han visitado
todos los nodos ).

Algoritmo en pseudocodigo:

1 método DFS( origen):
2 creamos una pila S
3 agregamos origen a la pila S
4 marcamos origen como visitado
5 mientras S no este vacío:
6 sacamos un elemento de la pila S llamado v
7 para cada vertice w adyacente a v en el Grafo: 
8 si w no ah sido visitado:
9 marcamos como visitado w
10 insertamos w dentro de la pila S

Código en C++:  Algoritmo DFS usando Stack

Usando Recursión
https://jariasf.wordpress.com/category/algorithms/ 26/45
21/10/2016 Algorithms | Algorithms and More
Usar la recursión es mucho mas fácil y ademas muy útil, es la forma mas usada en la solución de problemas con este
algoritmo.

Algoritmo en pseudocódigo:

1 método DFS( origen):
2 marcamos origen como visitado 
3 para cada vertice v adyacente a origen en el Grafo: 
4 si v no ah sido visitado:
5 marcamos como visitado v
6 llamamos recursivamente DFS( v )  

Tomemos como ejemplo el siguiente grafo no dirigido:

Al igual que con la pila requerimos un nodo inicial, de manera recursiva llamamos a los adyacentes del nodo inicial, de esta
forma se puede ver si llamamos inicialmente a “1”:

Inicial “1”: marcamos “1” como visitado, sus adyacentes son “2”,  “3” y “5”.

Visitados : 1.
Adyacentes de 1: 2 , 3 , 5

https://jariasf.wordpress.com/category/algorithms/ 27/45
21/10/2016 Algorithms | Algorithms and More

En la llamada recursiva ira el primero insertado en la lista de adyacencia, en este caso “2”, marcamos como visitado. Ahora el
inicial es “2”, sus adyacentes son “1” , “4” y “5”.

Visitados: 1 , 2
Adyacentes de 2: 1, 4 , 5

Evaluamos el 1ero de la lista que es “1” pero ya fue visitado por lo tanto sigo con el siguiente, en este caso “4” , marcamos
como visitado. Ahora inicial es “4”, sus adyacentes son “2”.

https://jariasf.wordpress.com/category/algorithms/ 28/45
21/10/2016 Algorithms | Algorithms and More
Visitados: 1 , 2 , 4
Adyacentes de 4: 2

Tocaria el nodo 2 pero ya fue visitado termina la recursion por ese lado. El siguiente adyacente de “2” es “5”. Ahora inicial es
“5”, marcamos como visitado, sus adyacentes son “1” y “2”.

Visitados: 1 , 2 , 4 , 5
Adyacentes de 5: 1 , 2

Igual que con nodo “4” sus adyacentes han sido visitados por lo tanto terminamos la recursion por el nodo “2”.

https://jariasf.wordpress.com/category/algorithms/ 29/45
21/10/2016 Algorithms | Algorithms and More
El nodo actual es “1”, sus adyacentes eran “2”, “5” y “3”, estabamos evaluando por “2” pero ya terminamos el siguiente es “5”
el cual ya fue visitado, continuamos con “3” este no fue visitado, marcamos como visitado, vemos sus adyacentes “1”.

Visitados: 1 , 2 , 4 , 5 , 3
Adyacentes de 3: 1

Como nodo “1” ya fue visitado entonces termina la recursión y termina el recorrido DFS. Como se puede observar el orden en
que fueron visitados los nodos es el recorrido DFS del grafo.

Posibles Paths en un grafo

https://jariasf.wordpress.com/category/algorithms/ 30/45
21/10/2016 Algorithms | Algorithms and More
Otra ayuda importantisima del algoritmo recursivo es que nos permite hallar todos los posibles paths( rutas) de un nodo
inicial a otro final, ello lo conseguiremos usando backtracking dentro del algoritmo:

Algoritmo en pseudocódigo:

1 método DFS( origen,final):
2 marcamos origen como visitado 
3 recuperar el path si se llego a final
4 para cada vertice v adyacente a origen en el Grafo: 
5 si v no ah sido visitado:
6 marcamos como visitado v
7 llamamos recursivamente DFS( v )
8 marcamos origen como no visitado

Codigo C++:  Algoritmo DFS usando Recursion

Componentes Conexas

Algun problema puede ser que nos pida hallar la cantidad de componentes conexas, supongamos el siguiente grafo no
dirigido:

La cantidad de componentes conexas es 2. El problema puede ser resuelto de la siguiente manera:

Evaluamos todos los vertices posibles y los tomamos como origen aquellos no visitados:

1 ...
2 for( int i = 1 ; i <= V ; ++i ){    //recorremos todos los posibles vertices
3     if( !visitado[ i ] ){          //si alguno no fue visitado tenemos una componente a partir de ese no
4        dfs( i );                  //recorremos a partir de nodo i todo el grafo que se forma
5        total++;                   //incrementamos cantidad de componentes
6     }
7 }
8 ...

Usamos el algoritmo estandar DFS para cada recorrido:


https://jariasf.wordpress.com/category/algorithms/ 31/45
21/10/2016 Algorithms | Algorithms and More
1 void dfs( int u ){
2     visitado[ u ] = true;
3     visitado_componente[ u ] = true;
4     for( int v = 0 ; v < ady[ u ].size(); ++v ){
5         if( !visitado[ ady[ u ][ v ] ] ){
6             dfs( ady[ u ][ v ] );
7         }
8     }
9 }

Veamos gráficamente, el código anterior parte evaluando el vértice 1:

i=1
Visitados: Ninguno

Como no fue visitado entonces hacemos recorrido DFS partiendo de ese vértice ( dfs( i = 1 ) ). Entonces veamos el recorrido:

Vértice Actual: 1
Vértices Adyacentes: 2
Vértices Visitados: 1

https://jariasf.wordpress.com/category/algorithms/ 32/45
21/10/2016 Algorithms | Algorithms and More

El siguiente paso es evaluar los adyacentes:

Vértice Actual: 2
Vértices Adyacentes: 3
Vértices Visitados: 1, 2

Vértice Actual: 3
Vértices Adyacentes: 2
Vértices Visitados: 1, 2, 3

https://jariasf.wordpress.com/category/algorithms/ 33/45
21/10/2016 Algorithms | Algorithms and More

Terminamos la función de DFS partiendo del vértice 1. Ahora volvemos al for inicial e incrementamos las componentes.

1 ...
2 if( !visitado[ i ] ){
3     dfs( i );
4     total++; //incrementamos numero de componentes
5 }
6 ...

Ahora tenemos i = 2 pero 2 ya fue visitado por lo que no entraría al if, igual para i=3 que ya fue visitado; sin embargo para
i=4 este no fue visitado por lo tanto hacemos recorrido dfs partiendo del vértice 4:

i=4
Visitados: 1, 2, 3

Vértice Actual: 4
Vértices Adyacentes: 5

https://jariasf.wordpress.com/category/algorithms/ 34/45
21/10/2016 Algorithms | Algorithms and More
Vértices Visitados: 1, 2, 3, 4

Como 5 no fue visitado es el siguiente a evaluar:

Vértice Actual: 5
Vértices Adyacentes: 4
Vértices Visitados: 1, 2, 3, 4, 5

https://jariasf.wordpress.com/category/algorithms/ 35/45
21/10/2016 Algorithms | Algorithms and More

Terminamos DFS partiendo del vértice 4, entonces volvemos al if e incrementamos el numero de componentes, de esta
manera el resultado será de 2 componentes.

Codigo C++: DFS Ejemplo – Conectividad

Ejemplo Aplicativo:

Tengamos un juego de dominos donde si hago caer uno domino, todos los demas dominos que siguen a este caerán. Dado el
numero de dominos “n”, el estado del juego en la forma “x y” ( si domino “x” cae entonces domino “y” tambien caerá) , la
cantidad de consultas a realizar, cada consulta sera el numero del domino el cual yo impulsaré. El problema me pide hallar
cuantos dominos caeran a partir del domino que yo impulsé.

Supongamos la entrada:

8 6 3  ( 8 dominos, 6 enlaces de dominos y 3 consultas  )

12

25

53

43

67

78

https://jariasf.wordpress.com/category/algorithms/ 36/45
21/10/2016 Algorithms | Algorithms and More
6

Solución

Declaremos nuestras variables globales:

1 vector<int> ady[ MAX ];                     //lista de adyacencia
2 int total;                                  //la cantidad total de dominos que caerán
3 bool visitado[ MAX ];                       //arreglo de domino caido

Modelemos el problema como un grado dirigido:

Ahora vemos cada consulta, la primera nos indica que impulsaré el domino numero 1, entonces al hacer ello los dominos que
caerán serán 1 -> 2 -> 5 ->3, debo retornar 4 la cantidad de dominos caidos. Para la segunda consulta caéran solamente 4->3,
y finalmente para 6 caerán 6->7->8

La solución lo relizaremos con un DFS como sigue:

1 void dfs( int u ){                          //domino origen
2     total++;                                //aumento en mi respuesta la caida de un domino
3     visitado[ u ] = true;                   //domino "u" cayo
4     for( int v = 0 ; v < ady[ u ].size(); ++v ){ //verifico los demas posibles domino que caeran si impu
5          if( !visitado[ ady[ u ][ v ] ] ){         //si el domino adyacente no cayó entonces es el sigui
6              dfs( ady[ u ][ v ] );               //recursivamente veo que dominos caeran a partir del ad
7          }
8     }
9 }

Codigo C++: DFS Ejemplo – Domino

A continuación algunos problemas que pueden ser resueltos con este algoritmo:

JUEZ UVA:

459 – Graph Connectivity

Varios Problemas se encuentran en el UvaToolkit colocando en el buscador DFS

JUEZ PKU:

2245 – Lotto

https://jariasf.wordpress.com/category/algorithms/ 37/45
21/10/2016 Algorithms | Algorithms and More
TOPCODER:

SRM 371: DIV 2 – 1000  FloodRelief

SRM 407: DIV 2 – 500 CorporationSalary

SRM 435 : DIV 1 – 250 CellRemoval

Se irán agregando mas adelante mas problemas de otros jueces asi como más aplicaciones de DFS.

Resumen de códigos:

Código en C++:  Algoritmo DFS usando Stack

Código en C++:  Algoritmo DFS usando Recursion

Código en C++: DFS Ejemplo – Domino

Código en C++: DFS Ejemplo – Conectividad

Por: Jhosimar George Arias Figueroa

Publicado en Algorithms, Main 10 comentarios


Etiquetado backtracking, depth first search, dfs, graph, search

ALGORITMO DE BÚSQUEDA: BREADTH FIRST SEARCH


Publicado el febrero 27, 2012| 18 comentarios

En esta oportunidad mostraré el algoritmo de búsqueda por anchura en un grafo (BFS) , se explicará el algoritmo y
propondremos algunos problemas que se solucionan con este algoritmo para que puedan resolverlos.

Descripción

Este algoritmo de grafos es muy útil en diversos problemas de programación. Por ejemplo halla la ruta más corta cuando el
peso entre todos los nodos es 1, cuando se requiere llegar con un movimiento de caballo de un punto a otro con el menor
numero de pasos, cuando se desea tranformar algo un numero o cadena en otro realizando ciertas operaciones como suma
producto, pero teniendo en cuenta que no sea muy grande el proceso de conversion, o para salir de un laberinto con el menor
numero de pasos , etc. Podrán aprender a identificarlos con la práctica.

Como trabaja

BFS va formando un árbol a medida que va recorriendo un grafo, veamos el ejemplo de la figura:

Si observan bien todo parte de un nodo inicial que será la raiz del árbol que se forma, luego ve los adyacentes a ese nodo y los
agrega en un cola, como la prioridad de una cola es FIFO (primero en entrar es el primero en salir), los siguientes nodos a

https://jariasf.wordpress.com/category/algorithms/ 38/45
21/10/2016 Algorithms | Algorithms and More
evaluar serán los adyacentes previamente insertados. una cosa bastante importante es el
hecho de que no se pueden visitar 2 veces el mismo nodo o Estado. ya que sino podriamos
terminar en un ciclo interminable o simplemente no hallar el punto deseado en el menor
número de pasos.

Dos ejemplos mas detallados lo puede ver desde aqui ->EJEMPLO1 BFS , EJEMPLO2 BFS

Algoritmo en pseudocódigo

1 método BFS(Grafo,origen):
2 creamos una cola Q
3 agregamos origen a la cola Q
4 marcamos origen como visitado
5 mientras Q no este vacío:
6 sacamos un elemento de la cola Q llamado v
7 para cada vertice w adyacente a v en el Grafo:
8 si w no ah sido visitado:
9 marcamos como visitado w
10 insertamos w dentro de la cola Q

Ejemplo Aplicativo:

Tenemos una matriz de caracteres que representa un laberinto 2D un ‘#’ implica un muro, un ‘.’ implica
un espacio libre, un ‘I’ indica la entrada del laberinto y una ‘S’ indica una salida.

¿Cuánto mide la ruta más corta para escapar?

Solución:

El problema nos da un Estado inicial y final, en este caso “I” y “S”, nos pide el menor numero de pasos para ir de “I” a “S”
tomando en consideración que solo podremos avanzar por los “.”

Dicha la descripción anteriro, el problema puede ser resulto por BFS al tener un estado inicial y final eh ir avanzando por
algun adyacente que no sea “#” . Los posibles adyacentes para movernos es hacia arriba, abajo, izquierda y derecha,
suponiendo que estamos en la posición (x,y) tendriamos:

https://jariasf.wordpress.com/category/algorithms/ 39/45
21/10/2016 Algorithms | Algorithms and More

Asi por ejemplo si estoy en la posición siguiente (circulo) y suponiendo que sea la coordenada 3,4 :

Los posibles Estados adyacentes serian: izquierda (3,2 -> “.”), derecha (3,5 -> “.”), arriba (2,4 -> “#”) y abajo (4,4 -> “#”) por
lo tanto para ese caso solo podriamos avanzar por la derecha o izquierda.

La solución la explicaremos paso a paso en el lenguaje C++ , para otros lenguajes como Java es bastante similar.

Primeramente declaramos nuestro laberinto como una matriz, tambien declaramos un arreglo de Visitado que nos indicará si
se visito o no un estado determinado:

1 ...
2 #define MAX 100 //máximo número de filas y columnas del laberinto
3 char ady[MAX][MAX];   //laberinto
4 bool visitado[MAX][MAX]; //arreglo de estados visitados
5 ...

Para la función BFS, la entrada que se recibe es el punto inicial de donde deseamos partir y el punto de llegada, en este
ejemplo el punto de llegada esta representado por “S” por lo que no es necesario pasarlo como parametro. El valor inicial
cuando trabajamos con dos dimensiones es necesario la fila y columna inicial, ademas tenemos que pasar la altura y el ancho
del laberinto para controlar los limites de recorrido estos podemos declararlos globales si se desea:

1 ...
2 int BFS(int x , int y, int h, int w)   //coordenadas de inicial "I" y dimensiones de laberinto
3  
4 ...

Cada vez que trabajamos con BFS trabajamos con estados, desde un estado inicial podemos llegar al estado final por medio
de varios estados, por ello una forma de manejar los diferentes estados y los valores que se tiene hasta ese estado podemos
realizar una estructura “Estado” que contendrá la posición del estado determinado asi como otros atributos, uno necesario es
la distancia en dicho estado:

1 struct Estado{
2   int x;  // Fila del estado
3   int y;  // Columna del estado
4   int d;  // Distancia del estado
https://jariasf.wordpress.com/category/algorithms/ 40/45
21/10/2016 Algorithms | Algorithms and More
5   Estado(int x1 , int y1 , int d1) : x(x1), y(y1), d(d1){}  // Constructor
6 };

Respecto a lo anterior otra forma para hallar las distancias sería ir almacenando en un arreglo de distancias. Declaramos
nuestro estado inicial:

1 ...
2 Estado inicial(x,y,0) ; //Estado inicial, distancia = 0
3 ...

Ahora para realizar un BFS es necesario la estructura de datos Cola, la cual pondemos implementar por medio de arreglos o
simplemente usar la estructura ya implementada de las librerias de C++ (queue) o Java(Queue):

1 ...
2 queue Q;   //Cola de todos los posibles Estados por los que se pase para llegar al destino
3 Q.push(inicial);   //Insertamos el estado inicial en la Cola.
4 ...

Nuestro arreglo de visitados es necesario inicializarlo como no visitado inicialmente:

1 ...
2 memset(visitado, false, sizeof(visitado)); //marcamos como no visitado
3 ...

Ahora a partir del inicial comprobamos si se llego al destino, en caso sea verdadero retornamos la distancia recorrida hasta
ese momento, de otro modo empezamos a evaluar los adyacentes y agregarlos a la cola. Para evaluar los adyacentes se explico
anteriormente que son dados en 4 direcciones ello se puede expresar de la siguiente manera:

1 ...
2 int dx[4] = {0, 0, 1, ‐1 };  //incremento en coordenada x
3 int dy[4] = {1, ‐1, 0, 0 };  //incremento en coordanada y
4 ...

Estos valores seran usados para aumentar al estado actual y ver su adyacente, esto se explico anteriormente en la grafica
ejemplo de recorrido.

Haremos todo este proceso descrito anteriormente mientras la cola no este vacia:

1 while( !Q.empty() ){                  //Mientras cola no este vacia
2    Estado actual = Q.front();         //Obtengo de la cola el estado actual, en un comienzo será el inic
3    Q.pop();                           //Saco el elemento de la cola
4    if( ady[actual.x][actual.y]== 'S'){//Si se llego al destino (punto final)
5      return actual.d;                 //Retornamos distancia recorrida hasta ese momento
6    }
7    visitado[actual.x][actual.y]=true; //Marco como visitado dicho estado para no volver a recorrerlo
8    for( int i = 0; i < 4; i++){       //Recorremos hasta 4 porque tenemos 4 posibles adyacentes
9       int nx = dx[i] + actual.x;      //nx y ny tendran la coordenada adyacente
10       int ny = dy[i] + actual.y;      //ejemplo en i=0 y actual (3,4) ‐> 3+dx[0]=3+0=3, 4+dy[0]=4+1=5, n
11       //aqui comprobamos que la coordenada adyacente no sobrepase las dimensiones del laberinto
12       //ademas comprobamos que no sea pared "#" y no este visitado
13       if(nx>=0 && nx<h && ny>=0 && ny<w && ady[nx][ny]!='#' && !visitado[nx][ny]){
14          Estado adyacente(nx, ny, actual.d+1);  //Creamos estado adyacente aumento en 1 la distancia rec
15          Q.push(adyacente);                     //Agregamos adyacente a la cola
16       }
17    }
18 }

Finalmente si no se pudo llegar al destino retornamos un valor que el problema comunmente nos da en este caso podemos
retornar -1.

Impresión del Camino Encontrado

https://jariasf.wordpress.com/category/algorithms/ 41/45
21/10/2016 Algorithms | Algorithms and More
Adicionalmente a lo anterior habrá problemas que nos pedirán imprimir la ruta que me llevo a la solución, por ello
tendremos un nuevo arreglo:

1 ...
2 Estado prev[ MAX ][ MAX ];     //Arreglo para mostrar la ruta que se siguio
3 ..

Este arreglo es de 2 dimensiones por que estamos trabajando con una malla de 2 dimensiones, la priera dimensión será para
las coordenadas X y la segunda será para coordenadas Y.

El arreglo me servirá para almacenar las coordenadas del Estado en el que estuve anteriormente, por ello se le puso como un
arreglo de Estado debido a que Estado posee las coordenadas X e Y que necesito.

Para el Estado inicial, al ser el primero no tengo un Estado anterior por lo que le pongo valores de -1:

1 ...
2 prev[ x ][ y ] = Estado( ‐1 , ‐1 , ‐1 );  //el inicial no tiene una ruta anterior puesto que es el prime
3 ..

Ahora veamos un ejemplo de por que me sirve un arreglo con las coordenadas anteriores:

Asumiendo que la salida del laberinto se llevo a cabo por medio de la ruta mostrada.

Una vez que llegue a S osea la salida, estoy en la coordenada 3,3. Para mostrar la ruta recorrida tengo almacenado en mi
arreglo prev el anterior de 3,3 que vendría a ser 3,2.

Osea accediendo al arreglo sería Estado ant = prev[ 3 ][ 3 ]. Si revisamos las coordenadas del Estado ant, tendríamos ant.x =
3, ant.y = 2. Y de esa manera iremos iterando viendo los anteriores hasta llegar al inicial donde ya no tengo anterior.

El valor del previo se actualiza al momento de insertar un nuevo Estado en la cola:

1 ...
2 if( nx >= 0 && nx < h && ny >= 0 && ny < w && ady[nx][ny] != '#' && !visitado[nx][ny] ){
3     ...
4     prev[ nx ][ ny ] = actual;               //El previo del nuevo nodo es el actual.
5 }
6 ..

La porción de código al estar en la coordenada 3,3 que es la posición de S es:

1 ...
2 if( ady[actual.x][actual.y]== 'S'){  //Si se llego al destino (punto final
3     return actual.d;                 //Retornamos distancia recorrida hasta ese momento

https://jariasf.wordpress.com/category/algorithms/ 42/45
21/10/2016 Algorithms | Algorithms and More
4 }
5 ..

A ello le agregaremos una función de impresión:

1 ...
2 if( ady[actual.x][actual.y]== 'S'){//Si se llego al destino (punto final
3     print( actual.x , actual.y );    //imprimo la ruta del camino mas corto
4     return actual.d;                 //Retornamos distancia recorrida hasta ese momento
5 }
6 ..

La función de impresión tendrá como parámetros las coordenadas del estado final, lo que tendremos que hacer es iterar hasta
encontrar el estado con valores -1, como se declaro en un inicio.

1 void print( int x , int y ){
2     //El arreglo prev posee las coordenadas del nodo anterior, por ello empezamos desde el final
3     //El proceso termina al momento de preguntar por el anterior del nodo inicial, como pusimos ‐1
4     //Preguntamos hasta que nuestro anterior sea diferente de ‐1
5     for( int i = x , j = y  ; prev[ i ][ j ].d != ‐1 ; i = prev[ x ][ y ].x , j = prev[ x ][ y ].y ){
6         ady[ i ][ j ] = '*'; x = i; y = j;
7     }
8  
9     printf("Camino con menor numero de pasos\n" );
10     for( int i = 0 ; i < h ; ++i ){
11         for( int j = 0 ; j < w ; ++j ){
12             printf("%c" , ady[ i ][ j ] );
13         }
14         printf("\n");
15     }
16 }

De esta manera se realiza la impresión de la ruta encontrada por el BFS, el arreglo prev se puede declarar de diferentes
formas acorde al programador.

A continuación dejamos una lista de problemas que pueden ser resueltos con este algoritmo:

Juez UVA

– 336 A Node Too Far

– 383 Shipping Routes

– 429 – Word Transformation

– 439 – Knight Moves

– 532 – Dungeon Master

– 627 – The Net

– 762 – We Ship Cheap

– 10653 – Bombs! NO they are Mines!!

– 11730 – Number Transformation

Para mas problemos en el UVA pueden encontrarlos en el UvaToolkit escribiendo en el buscador BFS.

https://jariasf.wordpress.com/category/algorithms/ 43/45
21/10/2016 Algorithms | Algorithms and More
Juez TJU

– 1056 – Labyrinth

– 1098 – The Separator Grid

– 2470 – Robot in Maze

– 2560 – The Explorer

– 2648 – Print Path

Juez HDU

– 1242 – Rescue

– 1195 – Open the Lock

– 1072 – Nightmare

– 3713 – Double Maze

– 1312 – Red and Black

– 1240 – Asteroids

Topcoder

– SRM 397 DIV 2, Problema de 500 puntos

– SRM 486 DIV 2, Problema de 500 puntos

– SRM 302 DIV 2, Problema de 900 puntos

ICPC LIVE

– 2040 – Multiple

– 2616 – I hate SPAM, but some people love it

– 3687 – Footbal Foundation (FOFO)

– 4461 – Easy Does It!

Códigos:

Implementación del algoritmo en Java: Algoritmo BFS

Implementación del algoritmo en C++: Algoritmo BFS

https://jariasf.wordpress.com/category/algorithms/ 44/45
21/10/2016 Algorithms | Algorithms and More
Ejemplo BFS en Java: Saliendo del Laberinto sin impresión de Ruta

Ejemplo BFS en C++: Saliendo del Laberinto con impresión de Ruta

Por Jhosimar George Arias Figueroa

Publicado en Algorithms, Main 18 comentarios


Etiquetado bfs, breadth first search, graph, search

Algorithms and More Blog de WordPress.com.

https://jariasf.wordpress.com/category/algorithms/ 45/45

Potrebbero piacerti anche