Está en la página 1de 66

GRAFOS

1. Definiciones bsicas:
Un grafo es la representacin por medio de conjntos de
relaciones arbitrarias entre objetos. !"isten dos tipos de grafos seg#n
la relacin entre los objetos sea n$%oca o bin$%oca. &os primeros
forman los grafos dirigidos o d$grafos ' los segndos los grafos no
dirigidos o simplemente grafos. !n la ma'or parte de los algoritmos
(e sern nestro objeto de estdio se )ace referencia a la
termolog$a bsica (e se propone a continacin. Dic)a
terminolog$a* por desgracia+ no es estndar ' pede llegar a %ariar en
los distintos te"tos (e e"isten en la materia. ,ando e"ista
ambig-edad se )arn las aclaraciones seg#n sea necesario.
Un grafo dirigido o d$grafo consiste de n conjnto de %.rtices
/ ' n conjnto de arcos A. &os %.rtices se denominan nodos o
pntos* los arcos tambi.n se conocen como aristas o l$neas dirigidas
(e representan (e entre n par de %.rtices e"iste na relacin
n$%oca aRb pero no bRa. De modo (e los arcos se representan
com#nmente por medio de pares ordenados 0a+b1+ donde se dice (e
a es la cabe2a ' b la cola del arco ' a mendo se representa tambi.n
por medio de na flec)a+ tal como se mestra en la figra 1.
Figra 1 Grafo dirigido
} { A V G + =
donde
} {
n
v v v V + + +
3 1
=
+
{ }
n
a a a A + + +
3 1
=
'
( )
k j i
v v a + =
tal
(e
V v v
k j
+
. !n dic)o grafo se entiende (e
( ) ( )
i j j i
v v v v + +
' en
mc)os casos solo e"iste no de los pares de %.rtices.
a b
Un %.rtice (e solo tiene arcos saliendo de .l se denomina
fente ' n %.rtice (e solo tiene arcos dirigidos )acia .l se
denomina smidero. Dic)a nomenclatra es importante cando los
d$grafos se san para resol%er problemas de fljos.
Un grafo no dirigido+ o grafo+ al igal (e n d$grafo consiste
de n conjnto de %.rtices / ' n conjnto de arcos A. &a diferencia
consiste en (e la e"istencia de aRb prespone (e bRa tambi.n
e"iste ' adems (e son igales. De este modo es indistinto )ablar
del arco 0a+b1 o 0b+a1+ tampoco tiene sentido )ablar de la cabe2a o la
cola del arco. &os grafos representan como lo indica la figra 3+
donde los c$rclos representan los %.rtices ' las l$neas representan
los arcos.
Figra 3 Grafo no dirigido
} { A V G + =
donde
} {
n
v v v V + + +
3 1
=
+
{ }
n
a a a A + + +
3 1
=
'
( )
k j i
v v a + =
tal
(e
V v v
k j
+
. !n dic)o grafo se entiende (e
( ) ( )
i j j i
v v v v + +
' adems
( ) ( )
i j j i
v v v v + + =
+ donde ambos pares de %.rtices representan el mismo
arco.
!"isten adems grafos en donde los arcos tienen asociado
alg#n %alor en c'o caso )ablamos de grafos ponderados ' a)ora se
representan los arcos como tripletas. Sige e"istiendo la informacin
de los %.rtices nidos por dic)o arco adems de la informacin del
peso de dic)o arco. As$ pes el arco se representa como
( ) w v v a
j i
+ + =

donde
j i
v v +
son el origen ' destino '
w
es el peso respecti%amente.
Un nodo b se dice (e es adyacente al nodo a si e"iste el arco
0a+ b1+ tmese en centa (e para n grafo no dirigido
necesariamente a es tambi.n ad'acente a b. !sto no ocrre en los
a b
grafos dirigidos donde la e"istencia de 0a+ b1 no implica (e 0b+ a1
tambi.n e"iste. !ste concepto es de particlar importancia dado (e
los grafos selen representarse en la comptadora por medio de
listas o matrices de ad'acencias.
Un arco 0a+b1 incide en el nodo b+ de igal modo en grafo no
dirigido dic)o arco tambi.n incide en el nodo a debido a (e
tambi.n e"iste 0b+ a1. !l n#mero de arcos (e inciden en n nodo le
otorga el grado a dic)o nodo. !l nodo con ma'or grado en el grafo
le indica el grado de dic)o grafo. 4ambi.n se acostmbra
representar a n grafo por medio de listas o matrices de incidencias.
!"isten otras definiciones (e son #tiles para e"plicar el
fncionamiento de n algoritmo en particlar+ se definirn los
conceptos en s momento.
3. 5.todos de representacin en comptadora
4al como se adelanto en el apartado anterior+ e"isten %arias
formas de representar n grafo en la comptadora ' cada na tiene
ss %entajas ' des%entajas. 5ostraremos las ms comnes ' la forma
de implementarlas.
&a primera forma es por medio de na matri2 de ad'acencias+
con este m.todo se tiene na matri2 de tama6o n"n+ donde n es el
nmero de %.rtices o nodos en el grafo. Una forma simple de %er la
informacin gardada en dic)a matri2 es (e los renglones de las
mismas representan el origen ' las colmnas el destino de cada
arista o arco en el grafo. Si el grafo es no ponderado se acostmbra
poner n cero en el 0rengln i+ colmna j1 de la matri2 cando no
e"iste dic)o arco ' n no cando dic)o arco e"iste en el grafo. !n
el caso de grafos ponderados+ se acostmbra poner na bandera
0normalmente el %alor de infinito1 en las posiciones donde no e"iste
n arco ' el peso correspondiente en las posiciones donde si e"iste.
1 2 3 4 5
1 7 1 7 7 1
2 1 7 1 1 1
3 7 1 7 1 7
4 7 1 1 7 1
5 1 1 7 1 7
Figra 8 Grafo no ponderado ' s matri2 de ad'acencia
Debe notarse (e para n grafo no dirigido la matri2 de
ad'acencia es sim.trica ' (e la diagonal principal contiene ceros.
!sto pede llegar a apro%ec)arse para a)orrar tiempo en algnos
algoritmos. &a representacin por medio de matri2 se prefiere para
algoritmos donde el nmero de arcos es grande en proporcin al
nmero de %.rtices. Si scediera lo contrario se prefiere la
representacin por medio de listas de ad'acencia.
Figra 9 &ista de ad'acencia para el grafo de la figra 8
&as estrctras de datos para las dos formas de representacin
anteriores peden modelarse en , como sige:
char grafo[MAX_VERT][MAX_VERT], visitado[MAX_VERT];
1 3
: 9
8
3 : ;
1 : 8 9 ;
3 9 ;
8 ; 3 :
9 1 3 ;
void inserta(char i, char j){
grafo[i][j] = grafo[j][i] = 1;
}
void limpia_grafo(){
int i, j;
for(i = 0; i < nvert; i++){
visitado[i] = 0;
for( j = i; j < nvert; j++)
grafo[i][j] = grafo[j][i] = 0;
}
}
&istado 1 Representacin por matri2 de ad'acencia
<ara encontrar los ad'acentes al %.rtice i se tendr$a (e
constrir n ciclo (e e%alara en el rengln i a(ellas colmnas
(e tienen n no. ,omo en el sigiente fragmento de cdigo+ donde
se (ieren meter los ad'acentes no %isitados a na pila.
for(i = 0; i < nvert; i++){
if(!visitado[i] && grafo[j][i]){
pila.push(i);
visitado[i] = 1;
}
}
&istado 3 !ncontrar ad'acentes al %.rtice j
!n las implementaciones de algoritmos se darn ms detalles
acerca del manejo de las estrctras de datos. <or a)ora re%isemos la
%ersin por medio de listas de ad'acencia.
#include <vector>
#include <list>
vector< list<int> > grafo(MAX_VERT);
char visitado[MAX_VERT];
void inserta_arista(int i, int j){
grafo[i].push_back(j);
grafo[j].push_back(i);
}
void limpia_grafo(){
int i;
for(i = 0; i < nvert; i++){
grafo[i].clear();
visitado[i] = 0;
}
}
list<int>::iterator aux, fin;
aux = grafo[j].begin();
fin = grafo[j].end();
while(aux != fin){
if(!visitado[*aux]){
pila.push(*aux);
visitado[*aux] = 1;
}
aux++;
}
&istado 8 /ersin por listas de ad'acencias
!n ambos casos se )a spesto n grafo no dirigido ' no
ponderado. !n el caso de n grafo dirigido basta con eliminar la
doble insercin ' no considerar la e"istencia de 0j+ i1 para cada 0i+ j1.
&a implementacin para grafos ponderados por medio de matrices se
presenta a continacin:
#define INFINITO MAXINT
char grafo[MAX_VERT][MAX_VERT], visitado[MAX_VERT];
void inserta_arista_ponderada(int i, int j, int w){
grafo[i][j] = w;
}
void limpia_grafo(){
int i, j;
for(i = 0; i < nvert; i++){
visitado[i] = 0;
grafo[i][i] = 0;
for( j = i+1; j < nvert; j++)
grafo[i][j] = grafo[j][i] = INFINITO;
}
}
int suma_pesos(int x, int y){
if( x == INFINITO || y == INFINITO) return INFINITO;
else return x + y;
}
&istado 9 Grafos ponderados por medio de matrices
Adicionalmente se mestra na fncin para smar pesos (e
permite solcionar el problema de smar aristas con %alor de
infinito. &o cal es m' com#n en algoritmos con grafos
ponderados.
A)ora podemos re%isar la %ersin con listas de ad'acencias.
<odemos notar (e es necesario tili2ar n par (e garde el nodo
destino adems del peso. A($ se define el primer miembro como el
destino ' el segndo como el peso.
#include <vector>
#include <list>
vector< list< pair<int , int> > > grafo(MAX_VERT);
char visitado[MAX_VERT];
void inserta_arista_ponderada(int i, int j){
pair ady;
ady.first = j;
ady.second = w
grafo[i].push_back(ady);
}
&istado : Grafos ponderados con listas de ad'acencia
!n mc)os casos es necesario ordenar las aristas de n grafo
ponderado de acerdo a s peso. Ante tal sitacin es apropiado
definir na estrctra (e contenga la informacin de las aristas '
lego insertarlas en na cola de prioridad. !n otras ocasiones+ se
desea formar n sbconjnto de aristas (e cmplen con na cierta
propiedad como cando se obtienen los rboles de e"pansin de los
recorridos de n grafo o se encentran los rboles de e"pansin
m$nima.
typedef pair< int , int > ARISTA
priority_queue< int, ARISTA> cola;
&istado = Definicin de tipos para grafos ponderados
5c)as %eces con%iene mltiplicar el peso por >1 para
con%ertir la pila de prioridad descendente de la S4& en na cola de
prioridad ascendente (e se necesita para algoritmos como dij?stra+
prim o ?rs?al. !"isten otras estrctras de datos (e son #tiles para
constrir algoritmos sobre grafos+ entre ellas estn las de conjntos
disjntos (e se disctirn ms adelante.
8. Algoritmos bsicos de b#s(eda
!"isten dos t.cnicas bsicas para recorrer los %.rtices de n
grafo+ la b#s(eda por profndidad 0DFS1 ' la b#s(eda por anc)ra
0@FS1. &a b#s(eda por profndidad se sa cando (eremos probar
si na solcin entre %arias posibles cmple con ciertos re(isitos
como scede en el problema del camino (e debe recorrer n
caballo para pasar por las =9 casillas del tablero. &a b#s(eda por
anc)ra se sa para a(ellos algoritmos en donde reslta critico
elegir el mejor camino posible en cada momento como scede en
dij?stra.
A continacin se mestra el algoritmo de la b#s(eda por
anc)ra en n grafo representado por medio de listas de
ad'acencias. !n dic)o algoritmo se sa na cola para almacenar los
nodos ad'acentes al actal ' gardarlos para continar con la
b#s(eda. !l sigiente listado contiene la implementacin del
recorrido por anc)ra para n grafo completamente conectado
0e"iste al menos n camino entre cal(ier par de %.rtices en el
grafo1 ' para n grafo (e no lo esta.
//algoritmo para grafo completamente conectado
void BFS(int v){//v es el nodo de inicio del recorrido
list<int> cola;//cola de adyacentes
list<int>::iterator nodo_actual, aux, fin;
visitado[v] = 1;//marcamos como visitado el nodo de inicio
cola.push_back(v);//metemos inicio a la cola
while(!cola.empty()){
nodo_actual = cola.front();//sacar nodo de la cola
cola.pop_front();
aux = grafo[nodo_actual].begin();//posicionar iteradores para
//lista de ady
fin = grafo[nodo_actual].end();
while(aux != fin){//recorrer todos los nodos ady a nodo actual
if(!visitado[*aux]){//aadir a la cola solo los no visitados
visitado[*aux] = 1;//marcarlos como visitados
cola.push_back(*aux);//aadirlos a la cola
//aqui podriamos aadir codigo para hacer algo mientras
//recorremos el grafo
}
aux++;//avanzar al siguiente adyacente del nodo actual
}
}
}
//algoritmo para grafo que no esta completamente conectado
void BFS2(){
int i;
for(i = 0; i < nvert; i++)
if(!visitado[i])
BFS(i);
}
&istado A @FS o recorrido por anc)ra
<ara (e el cdigo anterior fncione+ se deben declarar de
manera global el grafo ' el arreglo de %isitados. !l arreglo de
%isitados debe contener ceros antes de iniciar el recorrido en el
grafo. Dentro del ciclo (e a6ade los %.rtices reci.n %isitados a la
cola pede a6adirse cdigo para )acer algo mientras se recorre el
grafo+ n ejemplo de esto #ltimo lo peden encontrar en mi solcin
del problema 1777B+ donde se sa para asignar n ni%el a cada
cidad ' lego implementar n algoritmo Ad)oc para encontrar n
camino entre dos cidades. !l cdigo lo peden consltar en el
editor de la pgina de entrenamiento de la U45+ na%egando )asta la
carpeta jorge;1777B.
!l algoritmo de la b#s(eda por profndidad se pede )acer
modificando el anterior en la parte (e sa na cola ' sar na pila.
Otra forma de implementarla es sando recrsi%idad+ a continacin
se mestran ambos enfo(es as$ como la rtina para )acer la
b#s(eda en grafos (e no estn completamente conectados.
A continacin se presenta el listado con la implementacin de
la b#s(eda por profndidad o DFS. 4ambi.n se )a a6adido en la
%ersin recrsi%a n contador (e marca el orden en el (e feron
%isitados los nodos del grafo+ dic)o orden es m' #til al
implementar otros algoritmos de grafos.
void DFS(int v){//v es el nodo de inicio del recorrido
list<int> pila;//pila de nodos adyacentes
list<int>::iterator nodo_actual, aux, fin;
visitado[v] = 1;//marcar como visitado el nodo de inicio
pila.push_back(v);
while(!pila.empty()){//mientras no se vacie la pila de adyacentes
nodo_actual = pila.back();
//aqui podriamos marcar el orden en que se visitaron
pila.pop_back();
aux = grafo[nodo_actual].begin();//posicionar iteradores para
//lista ady
fin = grafo[nodo_actual].end();
while(aux != fin){//recorrer todos los ady al nodo actual
if(!visitado[*aux]){//aadir a la pila solo los no visitados
visitado[*aux] = 1;
pila.push_back(*aux);
//aqui podemos aadir cdigo para hacer algo mientras
//realizamos el recorrido
}
aux++;//avanzar al siguiente adyacente del nodo actual
}
}
}
//esta seria la versin recursiva del algoritmo anterior en cuyo caso no
se necesita la pila
void DFS(int v){
list<int>::iterator aux, fin;//iteradores para lista de ady
visitado[v] = 1;//marcar como visitado
//aqui se podria marcar el orden en que fueron visitados
aux = grafo[v].begin();//posicionar los iteradores para lista de ady
fin = grafo[v].end();
while(aux != fin){
if(!visitado[*aux])
DFS(*aux);//no se necesita marcar porque *aux se convierte en v
aux++;//avanzar al siguiente adyacente de v
}
}
//esta es la version para grafos que no estan completamente conectados
void DFS2(){
int i;
for(i = 0; i < nvert; i++)//buscar un nuevo nodo de inicio que no ha
sido visitado
if(!visitado[i])
DFS(i);
}
&istado C DFS o recorrido por profndidad
!s importante )acer notar (e los recorridos por anc)ra son
#tiles en a(ella aplicaciones en las (eremos encontrar el camino
ms corto entre cal(ier par de %.rtices ' es por ello (e forman la
base de dic)os algoritmos. !l recorrido por profndidad sir%e por
otro lado para a%erigar si n par de grafos estn conectados.
A continacin se mestra n algoritmo (e encentra n
camino a partir de los recorridos por profndidad ' por anc)ra.
Dic)os algoritmos se basan en lle%ar n registro del padre de cada
nodo en el recorrido+ eso se pede consegir agregando na l$nea
(e garde en n arreglo de padres cando se meten los %.rtices no
%isitados a la pila o cola de ad'acentes. ,omo sabemos (e cada
nodo (e se mete a la pila pro%iene del nodoDactal en el recorrido+
basta con a6adir na l$nea como padreEnodoDactalF G Ha"* dentro
del ciclo (e a6ade los nodos no %isitados a la pila o cola. Se
mestra como ejemplo la solcin del problema 1777B+ c'o listado
se incl'e a continacin.
#include<stdio.h>
#include<list>
#include<vector>
using namespace std;
int padre[26], visitado[26];//auxiliares para el recorrido
vector< list<int> > grafo(26);//almacenar el grafo
char c1[256], c2[256];//ciudades a explorar
int casos, aristas, consultas;
//funciones de soporte para manejar cada consulta y cada caso
//antes de cada caso se debe borrar el grafo
void borra_grafo(){//limpia las aristas del grafo para cada caso nuevo
int i;
for(i = 0; i < 26; i++)
grafo[i].clear();//borrar cada lista de adyacencia
}
//antes de cada recorrido se deben inicializar los padres y los visitados
void inicializa_busqueda(){//en cada consulta se inicializan los valores
int i;
for(i = 0; i < 26; i++){
padre[i] = -1;//los padres contienen a -1 como bandera de no hay padre
visitado[i] = 0;//todos los nodos se marcan como no visitados
}
}
//funcion de bsqueda por anchura que almacena el padre de cada nodo durante el
//recorrido
void BFS(int v){//recorre el grafo por anchura a partir de v
list<int> cola;//cola de adyacentes
list<int>::iterator aux, fin;//iteradores para recorrer adyacentes del
//nodo actual
int nact;//nodo actual
visitado[v] = 1;//se marca nodo v como visitado
cola.push_back(v);//se mete a la cola de adyacentes
while(!cola.empty()){
nact = cola.front();//se obtiene primer elemento de la cola
cola.pop_front();//se elimina dicho elemento
aux = grafo[nact].begin();//se obtienen iteradores a lista de nodo actual
fin = grafo[nact].end();
while(aux != fin){//mientras haya adyacentes al nodo actual
if(!visitado[*aux]){//se toman los nodos no visitados
visitado[*aux] = 1;//se marcan como visitados
padre[*aux] = nact;//se almacena el padre del nodo recien visitado
cola.push_back(*aux);//se mete dicho nodo a la cola
}
aux++;//tomar siguiente adyacente al nodo actual
}
}
}
//funcion que encuentra el camino a partir del origen v usado en el recorrido
//usando el arreglo de padres
void camino(int origen, int destino){//encuentra un camino de origen a destino
//usando el arreglo de padres y un procedimiento recursivo
if(origen == destino){//si se llego al caso base
printf("%c", origen + 'A');
}else{
if(padre[destino] == -1){//si no existe un camino hacia el origen
//desde el destino actual de llamada recursiva
printf("no existe camino de %c a %c\n", origen + 'A', destino + 'A');
}else{
camino( origen, padre[destino]);//se toma como caso mas simple
//de manera recursiva
printf("%c", destino + 'A');//se imprimen en orden inverso a
//partir del destino
}
}
}
//solucion del problema 10009 usando los algoritmos mencionados en el material
int main(){
int nodo1, nodo2;
scanf("%d\n", &casos);//leer numero de casos
while(casos>0){
scanf("%d %d\n", &aristas, &consultas);//leer numero de aristas y
//de consultas
fflush(stdin);
borra_grafo();//limpiar el grafo antes de leer las aristas
while(aristas > 0){//leer las aristas y almacenarlas en el grafo
scanf("%s %s\n", c1, c2);//leer arista como ciudad1 ciudad2
fflush(stdin);
nodo1 = c1[0] - 'A';//encontrar posicion en la lista de
nodo2 = c2[0] - 'A';//adyacentes en el grafo
grafo[nodo1].push_back(nodo2);//mete la arista en grafo no
grafo[nodo2].push_back(nodo1);//dirigido
aristas--;//actualizar numero de aristas por leer
}
while(consultas > 0){//leer las consultas
scanf("%s %s\n", c1, c2);//leer origen y destino de
//la consulta
fflush(stdin);
nodo1 = c1[0] - 'A';//encontrar posiciones en la lista de
nodo2 = c2[0] - 'A';//adyacentes
inicializa_busqueda();//borra los arreglos antes de
//iniciar la busqueda
BFS(nodo1);//encuentra los caminos a partir de nodo1(origen)
camino( nodo1, nodo2);//encontrar el camino de la ciudad1 a
//la ciudad2
printf("\n");
consultas--;//actualizar el numero de consultas por realizar
}
casos--;//actualizar el numero de casos por resolver
if(casos>0)//para no imprimir el ultimo enter
printf("\n");
}
return 0;
}
&istado B ,aminos a partir del recorrido en anc)ra
!n el listado anterior se sa como base el )ec)o de (e los
recorridos por anc)ra o profndidad eligen n camino #nico para
cada destino alcan2able a partir de n origen+ dic)o camino se pede
reconstrir a partir de las aristas 0padreEiF+ i1 (e se eligieron drante
el recorrido. !l camino es #nico por(e los recorridos prodcen na
estrctra de rbol con las aristas seleccionadas. &os ciclos se
eliminan por medio del arreglo de nodos pre%iamente %isitados+ los
cales se descartan drante el recorrido. !l problema 1777B tambi.n
pede ser reselto si se sstit'e la b#s(eda por anc)ra sando
na b#s(eda por profndidad por(e las condiciones del problema
)acen (e los rboles resltantes sean id.nticos.
&as b#s(edas son na parte m' importante de los algoritmos
sobre grafos ' mc)os de ellos son constridos a partir de ellos.
Algnos permiten clasificar ' encontrar propiedades interesantes de
los grafos como los ciclos ' los pntos de articlacin. !n el
sigiente apartado se mostrarn algoritmos basados en algna
%ariacin de los recorridos ' se pedir )acer referencia a esta
seccin con el fin de enfocarse en la t.cnica ne%a sin e"plicar
ne%amente el es(ema general del recorrido sado.
9. 4eorema de los par.ntesis ' ss aplicaciones
Drante n recorrido en profndidad es posible almacenar el
tiempo en (e se %isita n nodo por primera %e2+ cando se )an
terminado de recorrer todos los nodos ad'acentes a dic)o nodo ' los
momentos en los (e se %el%e a %isitar. ,on dic)a informacin es
posible )acer na clasificacin de las aristas sadas para constrir el
recorrido en aristas del rbol de recorrido+ aristas de retroceso+
aristas de crce ' de a%ance. &as primeras son a(ellas aristas 0+ %1
(e se toman cando se retira de la pila el nodo ' se detecta (e el
nodo % no )a sido %isitado a#n. &as aristas de retroceso son a(ellas
ad'acentes al nodo (e se saca de la pila ' (e 'a )an sido %isitadas+
lo (e indica la presencia de n ciclo. &as aristas de crce ' de
a%ance resltan de a(ellos (e no se encentran completamente
conectados cando e"iste na arista de no de los rboles del bos(e
de recorrido+ )acia otro de los rboles pre%iamente %isitados. !ste
tipo de aristas resltan com#nmente en los grafos dirigidos.
!l teorema de los par.ntesis dice (e los tiempos de inicio '
finali2acin de la %isita de n nodo ' ss ad'acentes forman na
estrctra de par.ntesis perfectamente anidados. !sta estrctra de
par.ntesis representa de algna forma el rbol de recorrido+ an(e
tambi.n pede representar n bos(e. Dic)a informacin se sa para
algoritmos como el de los componentes cone"os ' los pntos de
articlacin. Otra aplicacin reslta de la ordenacin topolgica de
los %.rtices en el grafo. Ambos algoritmos se presentan a
continacin en forma de sedocdigo ' lego se sgiere na forma
de implementarlos.
!l algoritmo del ordenamiento topolgico resel%e el problema
de encontrar el orden en (e se deben lle%ar a cabo na serie de
acti%idades cando e"isten re(isitos de acti%idades pre%ias a
reali2ar como pede ser la crricla de materias a estdiar en na
ni%ersidad. 5c)as acti%idades re(ieren de dic)a planeacin '
por lo reglar se aplican en el manejo de pro'ectos de di%ersa $ndole
como peden ser in%ersiones+ manfactra ' constrccin.
Simplemente el %estirse re(iere (e se siga na secencia
apropiada* por ejemplo+ nadie se pone los calcetines desp.s de
ponerse los 2apatos.
ORD!IA5J!I4OD4O<O&OGJ,O0G1
DFS0G1 ' calclar fE%F para cada % en G
,onforme se termina de %isitar n %.rtice insertar al frente de
na lista
De%ol%er la lista como resltado
&os tiempos de finali2acin ' de inicio de %isita de n %.rtice
se encentran lle%ando n contador (e indi(e el orden en el (e se
)acen las llamadas ' almacenando dic)o contador en n arreglo. &a
idea se mestra a continacin a manera de sedocdigo.
DFS0G1
<ara cada nodo en G
o colorEF G blanco
o padreEF G IU&O
tiempo G 7
<ara cada nodo en G
o Si colorEF G blanco DFS>/JSJ401
DFS>/JSJ401
colorEF G gris ;;se acaba de %isitar el nodo por primera %e2
tiempo G tiempo K 1
dEF G tiempo
<ara cada nodo % (e se ad'acente a ;;e"plorar aristas 0+%1
o si colorE%F G blanco
padreE%F G
DFS>/JSJ40%1
colorEF G negro ;;se termino de %isitar al nodo ' todos ss
ad'acentes
tiempo G tiempo K 1
fEF G tiempo
!n los algoritmos anteriores se )an sado ideas 'a e"pestas
en el apartado anterior. <or lo (e solo resta )acer algnas
obser%aciones. &a primera de ellas es (e el arreglo color se sa de
manera similar a %isitados con el fin de detectar los nodos
pre%iamente %isitados ' a)ora se )an definido tres estados
diferentes+ sin %isitar corresponde al color blanco+ reci.n %isitado
corresponde al gris ' negro indica (e se )a terminado de e"plorar la
rama del rbol (e contiene a nodo en particlar. &a %ariable tiempo
se sa para determinar el orden consecti%o en (e se %isitan los
nodos ' dic)a informacin se almacena en el arreglo d. !l arreglo
padre se sa para identificar las aristas (e forman parte del rbol de
recorrido ' para reconstrir los caminos a partir de la rai2 de dic)os
rboles. Debe notarse (e n nodo se marca como negro )asta (e
todos ss nodos ad'acentes )an terminado de recorrerse ' tienen el
estado de gris o negro. !l arreglo f almacena el momento en (e los
nodos se marcaron como negros. &a informacin de los recorridos
tal como se mestran a($ fe tomada del capitlo 33 del libro de
,ormen 3da edicin. &a implementacin de los algoritmos
anteriores+ sando c ' la S4&+ se mestra a continacin.
#include<stdio.h>
#include<vector>
#include<list>
using namespace std;
vector< list<int> > grafo(10);//representacion del grafo
int padre[10], color[10], d[10], f[10], tiempo;//variables de los algoritmos
#define BLANCO 0 //estados del nodo durante el recorrido
#define GRIS 1
#define NEGRO 2
#define NULO -1 //bandera para indicar que no se conoce al padre de un nodo
void limpia_grafo();
void DFS();
void DFS_VISIT(int);
//programa de prueba para los algoritmos DFS que aparecen en el cormen
int main(){
//variables para capturar el grafo
int na, origen, destino, i;
//capturar el numero de aristas en el grafo
scanf("%d\n", &na);
limpia_grafo();
while(na){
scanf("%d %d\n", &origen, &destino);
grafo[origen].push_back(destino);
na--;
}
//se llama al procedimiento de busqueda
DFS();
//se imprime el arreglo de descubrimientos
printf("arreglo d\n");
for(i = 0; i < 6; i++)
printf("d[%d] = %d, ", i, d[i]);
printf("\n");
//se imprime el arreglo de finalizaciones
printf("arreglo f\n");
for(i = 0; i < 6; i++)
printf("f[%d] = %d, ", i, f[i]);
printf("\n");
return 0;
}
//limpiar el grafo antes de capturar los datos
void limpia_grafo(){
int i;
for( i = 0; i < 6; i++)
grafo[i].clear();
}
//implementacion de los algoritmos tal como aparecen en el libro de cormen
void DFS(){
int u;
//inicializar las variables antes del recorrido
for( u = 0; u < 10; u++){
color[u] = BLANCO;
padre[u] = NULO;
}
tiempo = 0;
//recorrido para grafos en general(no completamente conectados)
for( u = 0; u < 6; u++)
if( color[u] == BLANCO )
DFS_VISIT(u);
}
//version recursiva del DFS que lleva cuenta de los tiempos de descubrimiento y
//finalizacin, para la demostracin del teorema de los parentesis
void DFS_VISIT(int u){
//iteradores para manejar la lista de adyacentes a u
list<int>::iterator v, fin;
color[u] = GRIS;
tiempo++;
d[u] = tiempo;
//iniciar con la visita de los adyacentes a u
for(v = grafo[u].begin(); v != grafo[u].end(); v++){
if(color[*v] == BLANCO){
padre[*v] = u;
DFS_VISIT(*v);
}
}
color[u] = NEGRO;
tiempo++;
f[u] = tiempo;
}
&istado 17 @FS como aparece en el cormen
!l algoritmo anterior se prob con el grafo de la figra :.
&ego se mestra la salida del programa anterior donde se mestra
el tiempo de descbrimiento ' finali2acin de cada nodo en el grafo.
Figra : Grafo para probar listado 17
&a salida del programa del listado 17 es la sigiente:
[jorge@localhost ~]$ ./dfs_cormen<dfs_cormen.in
arreglo d
d[0] = 1, d[1] = 2, d[2] = 9, d[3] = 4, d[4] = 3, d[5] = 10,
arreglo f
f[0] = 8, f[1] = 7, f[2] = 12, f[3] = 5, f[4] = 6, f[5] = 11,
7 1 3
8 9 :
Si ordenamos los tiempos de descbrimiento ' finali2acin+
escribiendo los n#meros de nodos+ tendremos la sigiente secencia
(e ilstra el teorema de los par.ntesis.
07 01 09 08 81 91 11 71 03 0: :1 31
Dic)a secencia indica (e como el resltado del recorrido se
)a formado n bos(e con 3 arboles+ el primero contiene a los nodos
7+ 1+ 9 ' 8+ el segndo contiene a los nodos 3 ' :.
A)ora estamos en la posibilidad de mostrar la implementacin
del ordenamiento topolgico+ tal como aparece en sedocdigo
mostrado anteriormente. Se mestran #nicamente las modificaciones
(e es necesario )acer al DFSD/JSJ4+ para almacenar la lista con el
orden de finali2acin.
//cdigo modificado para almacenar el orden de finalizacin requerido para el
//odenamiento topologico
void DFS_VISIT(int u){
//iteradores para manejar la lista de adyacentes a u
list<int>::iterator v, fin;
color[u] = GRIS;
tiempo++;
d[u] = tiempo;
//iniciar con la visita de los adyacentes a u
for(v = grafo[u].begin(); v != grafo[u].end(); v++){
if(color[*v] == BLANCO){
padre[*v] = u;
DFS_VISIT(*v);
}
}
color[u] = NEGRO;
tiempo++;
f[u] = tiempo;
orden.push_front(u);//insertar cada vrtice en el orden en que finaliza
}
void ORDENAMIENTO_TOPOLOGICO(){
list<int>::iterator aux;//iterador para recorrer la lista con resultados

orden.clear();//borrar la lista de ordenamiento
dfs();//calcular los f[u] del grafo
//imprimir los resultados
for( aux = orden.begin(); aux != orden.end(); aux++)
printf(%d, , *aux);
printf(\n);
}
&istado 11 5odificaciones a DFS para ordenamiento topolgico
<ara (e el cdigo anterior fncione+ se necesita (e la lista
(e almacenar el ordenamiento sea declarada de manera global. !n
el cdigo anterior la %ariable tipo lista de enteros se llama orden.
,ando se preba con el grafo de la figra 33.A de la segnda
edicin del cormen+ el resltado es el sigiente:
[jorge@localhost ~]$ ./ord_topo<ord_topo.in
8, 6, 3, 4, 0, 1, 7, 2, 5,
0 = undershorts, 1 = pants, 2 = belt, 3 = shirt, 4 = tie, 5 =
jacket, 6 = socks, 7 = shoes, 8 = watch
A#n cando el resltado es diferente al (e aparece en el libro+
no se altera el orden correcto (e se necesita para colocarse encima
las prendas. &a ra2n por la (e se e"plica la diferencia es el orden
en el (e se enmeran los nodos ' lego se %isitan.
A continacin se lista el sedocdigo del algoritmo (e
encentra los componentes fertemente conectados del grafo. <or
definicin n componente fertemente conectado de n grafo es n
sbgrafo , en el (e para cada par de %.rtices ' % en ,+ e"iste n
camino de a % ' de % a . !l algoritmo se basa en el lema (e
enncia (e los componentes fertemente conectados de n grafo
corresponden a los de s transpesto. !l grafo transpesto 4 de n
grafo G+ es el mismo conjnto de %.rtices pero con las direcciones
de las aristas en sentido contrario+ es decir la arista 0+ %1 en G
corresponde a 0%+ 1 en 4.
S,,0G1
1. llamar DFS0G1 para calclar los fEF para cada en G
3. encontrar 4
8. llamar DFS041+ pero en el ciclo principal de DFS+ los %.rtices
se e"ploran en orden decreciente del fEF calclado en el paso
1
9. Sacar los %.rtices de cada rbol generado en el paso 8 como n
componente fertemente cone"o por separado.
A continacin se mestran las modificaciones necesarias al
DFS para implementar el algoritmo anterior. Debe notarse (e el
orden decreciente de los fEF calclados en el paso 1+ corresponden
al ordenamiento topolgico de los %.rtices en G. <or lo (e el paso 1
se pede sstitir por obtener el ordenamiento topolgico de G. L en
el paso 8 dir$amos (e se recorre el grafo en profndidad sando el
ordenamiento topolgico calclado en el paso 1. 4ambi.n se incl'e
na implementacin para encontrar el transpesto de n grafo.
//transpuesto de un grafo G con nv vertices, el resultado es el grafo T
void transpuesto(){
list<int>::iterator aux;
int i;
//borrar el grafo T antes de comenzar
for(i = 0; i < nv; i++)
T[i].clear();
for(i = 0; i < nv; i++){
for(aux = G[i].begin(); aux != G[i].end(); aux++)
T[*aux].push_back(i);
}
}
//ordenamiento topologico modificado para que no imprima el orden solo lo calcula
void ORDENAMIENTO_TOPOLOGICO(){
list<int>::iterator aux;
orden.clear();//borrar la lista
//calcular los f[u] con el DFS
DFS();
}
//encuentra los componentes fuertemente conectados sobre T
void SCC(){
int u;
list<int> aux;
//inicializar las variables antes del recorrido
for( u = 0; u < 10; u++){
color[u] = BLANCO;
padre[u] = NULO;
}
tiempo = 0;
//visitar T usando el orden topologico de G
ORDENAMIENTO_TOPOLOGICO();
transpuesto();
for( aux = orden.begin(); aux != orden.end(); aux++)
if( color[*aux] == BLANCO ){
DFS_VISIT2(*aux);
printf("%d");//termino con un SCC
}
}
//busqueda sobre el grafo T e impresin de los elementos de cada SCC
void DFS_VISIT2(int u){
//iteradores para manejar la lista de adyacentes a u
list<int>::iterator v, fin;
color[u] = GRIS;
tiempo++;
d[u] = tiempo;
printf("%d, ", u);//imprime los elementos del SCC
//iniciar con la visita de los adyacentes a u
for(v = T[u].begin(); v != T[u].end(); v++){
if(color[*v] == BLANCO){
padre[*v] = u;
DFS_VISIT(*v);
}
}
color[u] = NEGRO;
tiempo++;
f[u] = tiempo;
}
&istado 13 ,alclo de los S,,
!n el cdigo anterior se deben declarar los grafos G ' 4 como
%ariables globales. Se deja como ejercicio )acer la implementacin
completa ' probar con el grafo de la figra 33.17 del libro de
cormen.
Adicionalmente al cdigo anterior+ es posible reali2ar na
implementacin (e localice los pntos de articlacin en n grafo a
partir de s recorrido en profndidad ' del orden en (e se %isitan
ss nodos con la a'da de la fncion &OM. Dic)o algoritmo se
describe en la figra :.11 de A)o+ Nopcroft+ Ullman O4)e design
and anlisis of compter algorit)msP. Se transcribe a continacin
en el sigiente sedocdigo:
void SEARCHB(v){
marcar v como visitado
dfs_number[v] = cont;
cont++;
LOW[v] = dfs_number[v];
para cada vertice w adyacente a v{
si no ha sido visitado w{
aadir (v, w) al arbol T;
padre[w] = v;
SEARCHB(w);
si LOW[w] >= dfs_number[v] se encontro componente;
LOW[v] = min(LOW[v], LOW[w]);
}de otra forma{
si w no es el padre de v
LOW[v] = min(LOW[v], dfs_number[w]);
}
}
}
!n la l$nea donde se encontr n componente+ se pede %aciar
la lista 4 de los %.rtices % (e forman parte de n componente o
imprimir los %.rtices % (e corresponden a los pntos de articlacin
en el grafo+ con e"cepcin de la ra$2 del rbol de b#s(eda. &a
implementacin del algoritmo anterior se lista a continacin:
#include <stdio.h>
#include <vector>
#include <list>
using namespace std;
vector< list<int> > grafo(30);
int LOW[30], visitado[30], dfs_number[30], padre[30];
int i, cont, n;
int origen, destino;
list< pair<int , int> > T;
list<int> articulaciones;
void limpia_grafo(){
cont = 0;
for(i = 0; i < 30; i++){
LOW[i] = visitado[i] = dfs_number[i] = padre[i] = 0;
grafo[i].clear();
}
T.clear();
articulaciones.clear();
}
void searchb(int v){//implementacin del algoritmo de busqueda de puntos
list<int>::iterator aux, fin;//de articulacin
pair<int, int> arista;
visitado[v] = 1;
dfs_number[v] = cont;
cont++;
LOW[v] = dfs_number[v];
aux = grafo[v].begin();
fin = grafo[v].end();
arista.first = v;
while(aux != fin){
if(!visitado[*aux]){
arista.second = *aux;
T.push_back(arista);
padre[*aux] = v;
searchb(*aux);
if(LOW[*aux] >= dfs_number[v])
articulaciones.push_back(v);
LOW[v] = min( LOW[v], LOW[*aux]);
}else{
if(*aux != padre[v])
LOW[v] = min( LOW[v], dfs_number[*aux]);
}
aux++;
}
}
int main(){
list<int>::iterator aux, fin;
limpia_grafo();
scanf("%d\n", &n);
while(n>0){
scanf("%d %d\n", &origen, &destino);
grafo[origen-1].push_back(destino-1);
grafo[destino-1].push_back(origen-1);
n--;
}
searchb(0);
aux = articulaciones.begin();
fin = articulaciones.end();
printf("articulaciones\n");
while(aux != fin){
n = i + 1;
printf("%d\n",*aux + 1);
aux++;
}
return 0;
}
&istado 13 Jmplementacin de pntos de articlacin
!n el cdigo anterior )ace falta %alidar (e la ra$2 del rbol de
b#s(eda en profndidad no siempre es n pnto de articlacin. &a
ra$2 siempre tiene n dfsDnmber igal a cero ' es por ello (e
aparece como pnto de articlacin. !s de notarse (e todos los
miembros+ a e"cepcin del pnto de articlacin+ de n componente
tienen el mismo %alor de &OM.
:. Qrboles de e"pansin m$nima
!n ocasiones se presenta el problema de elegir no de %arios
rboles de e"pansin (e cmplan con el re(isito de (e la sma
total del peso de ss %.rtices sea la m$nima posible. !ste es n
problema de optimi2acin en donde se bsca redcir el costo total de
nir na serie de pntos en n grafo+ por ejemplo pede desearse
nir con caminos n conjnto de cidades de tal forma (e la
longitd total de los caminos a constrir sea el m$nimo ' (e adems
permita (e todas est.n conectadas. !"isten na serie de algoritmos
basados en na t.cnica de programacin %ida (e cmplen con
dic)o re(isito+ nos enfocaremos particlarmente en dos de ellos+ el
algoritmo de Rrs?al ' en el de <rim.
!l algoritmo de Rrs?al basa s fncionamiento en la eleccin
de las aristas de menor peso (e no forman ciclos+ para poder elegir
dic)as aristas es necesario sar n m.todo de almacenamiento (e
las ordene de menor a ma'or peso. Dado (e dic)o m.todo iniciar
eligiendo cal(ier arista (e cmpla con el re(isito de tener el
menor peso ' (e no forme ciclos+ es necesario mantener na serie
de conjntos disjntos por lo (e s implementacin )ace so de la
estrctra UIJOI>FJID recomendada por el libro de cormen ' (e
aparece dentro de s propio apartado dentro de la seccin estrctras
del temario. !l sedocdigo del Rrs?al se mestra a continacin:
5S4>RRUSRA&0G+S1
1. A es el conjnto %ac$o
3. para cada %.rtice % en G
make-set(v)
8. ordenar las aristas de menor a ma'or peso
9. para cada arista 0+%1 en G+ en tomadas en orden creciente
si find-set(u) es diferente de find-set(v)
o A es la nion de A con 0+%1
o union(u,v)
:. De%ol%er A
&as operaciones en negritas corresponden a la implementacin
de UIJOI>FJID. !l resltado del algoritmo es el rbol de
e"pansin representado por el conjnto de aristas inclidas en A. A
continacin se mestra na implementacin basada en la S4&. !sta
implementacin se prob con el grafo (e aparece en la figra 38.9
del libro de ,ormen.
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
//implementacin de UNION-FIND
#define MAX 1000 // ajustarlo apropiadamente (tamao mximo del conjunto)
int p[MAX], rank[MAX];
void make_set(int x)
{
p[x] = x;
rank[x] = 0;
}
void link(int x, int y)
{
if (rank[x] > rank[y])
p[y] = x;
else
{
p[x] = y;
if (rank[x] == rank[y])
rank[y] = rank[y] + 1;
}
}
int find_set(int x)
{
if (x != p[x])
p[x] = find_set(p[x]);
return p[x];
}
void union_set(int x, int y)
{
link(find_set(x), find_set(y));
}
//definiciones para usar en el algorimo de Kruskal
#define ARISTA pair<int, int>
#define ARISTA_PONDERADA pair<int, ARISTA>
int nvert, narist;
//representacin del grafo con un vector de aristas
vector<ARISTA_PONDERADA> G(14), A(14);//14 aristas para la prueba
//algoritmo de kruskal
void kruskal(){
ARISTA a;
int i, j;//contadores de aristas y vertices
int u, v;//vertices
for(v = 0; v < nvert; v++)
make_set(v);
sort(G.begin(), G.end());
for(i = 0, j = 0; i < narist; i++){
a = G[i].second;
u = a.first;
v = a.second;
if(find_set(u) != find_set(v)){
A[j].first = G[i].first;
A[j++].second = a;
union_set(u,v);
}
}
}
int main(){
int i, n;//contadores
int u, v, w;//datos de las aristas
ARISTA a;//arista
ARISTA_PONDERADA ap;//arista ponderada
//programa de prueba para el algoritmo de kruskal
scanf("%d %d\n", &nvert, &narist);//leer numero de aristas y vertices
n = narist;//iniciar los contadores
i = 0;
while(n){//ciclo para leer las aristas
scanf("%d %d %d\n", &u, &v, &w);
a.first = u;
a.second = v;
ap.first = w;
ap.second = a;
G[i++] = ap;
n--;
}
for(i = 0; i < narist; i++)//ciclo para marcar las aristas
A[i].first = -1;

//se manda a llamar a kruskal
kruskal();
//se imprimen los resultados
printf("arbol resultante\n");
for(i = 0; i < narist; i++){
if(A[i].first != -1){
ap = A[i];
a = ap.second;
u = a.first;
v = a.second;
w = ap.first;
printf("(%d, %d, %d)\n", u, v, w);
}
}
return 0;
}
&istado 18 Jmplementacin de Rrs?al
A continacin se presenta el so del algoritmo de Rrs?al
para la solcin del problema 178BA ,onnect t)e ,amps del je2
en l$nea de la U/A. A($ se sa ?rs?al para (e sme las
distancias entre los pntos ' encentre la distancia total. Se sa
UIJOI>FJID para inclir los caminos 'a constridos. <ara e%itar
problemas con las comparaciones ' errores de precisin+ las
distancias se almacenan como enteros ' lego se calcla la rai2
cadrada. 4ambi.n se sobrecargo el operador de comparacin para
(e )iciera correctamente las comparaciones de los pesos de las
aristas. &a solcin aceptada es la sigiente:
#include<stdio.h>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
//implementacin de UNION-FIND
#define MAX 1000 // ajustarlo apropiadamente (tamao mximo del conjunto)
int p[MAX], rank[MAX];
int nconj;
void make_set(int x)
{
p[x] = x;
rank[x] = 0;
}
void link(int x, int y)
{
if (rank[x] > rank[y])
p[y] = x;
else
{
p[x] = y;
if (rank[x] == rank[y])
rank[y] = rank[y] + 1;
}
}
int find_set(int x)
{
if (x != p[x])
p[x] = find_set(p[x]);
return p[x];
}
void union_set(int x, int y)
{
link(find_set(x), find_set(y));
nconj--;//disminuye el numero de conjuntos con cada union
}
//definiciones para usar en el algorimo de Kruskal
#define ARISTA pair<int, int>
#define ARISTA_PONDERADA pair< long, ARISTA>
int nvert, narist;
long double longitud;
//representacin del grafo con un vector de aristas
vector<ARISTA_PONDERADA> G;
//sobrecarga del operador menor para comparar
class LessWeightedEdge{
public:
bool operator()(const ARISTA_PONDERADA &p, const ARISTA_PONDERADA &q) const{
return (p.first < q.first);
}
};
//algoritmo de kruskal
void kruskal(){
ARISTA a;
int i;//contadores de aristas y vertices
int u, v;//vertices

//cuando se ordena lo hace con enteros
sort(G.begin(), G.end(), LessWeightedEdge());
longitud = 0;
//revisa todas las aristas o hasta que se forma un conjunto unico
for(i = 0; (i < narist) && (nconj > 1); i++){
a = G[i].second;
u = a.first;
v = a.second;
if(find_set(u) != find_set(v)){
//aqui si se calcula la raiz
longitud = longitud + sqrtl(G[i].first);
union_set(u,v);
}
}
}
//para evitar errores de precision se almacena antes de calcular la raiz
double distancia(long x1, long y1, long x2, long y2){
long dif1, dif2;
dif1 = x1 - x2; dif2 = y1 - y2;
return dif1*dif1 + dif2*dif2;
}
int main(){
vector< pair<int, int> > edificios;
pair<int , int> edificio;
int i, j, x, y, existentes;
int x1, y1, x2, y2;
int u, v;
long w;
ARISTA a;
ARISTA_PONDERADA ap;
while(scanf("%d\n", &nvert) != EOF){//leer todos los casos

//borrar el vector de edificios
edificios.clear();

//leer la posicion de todos los edificios y almacenar en el vector
for(i = 0; i < nvert; i++){
scanf("%d %d\n", &x, &y);
edificio.first = x;
edificio.second = y;
edificios.push_back(edificio);
}

//borrar el grafo
G.clear();
//generar el grafo
for(i = 0, narist = 0; i < nvert; i++){
x1 = edificios[i].first;
y1 = edificios[i].second;
for(j = i+1; j < nvert; j++){
x2 = edificios[j].first;
y2 = edificios[j].second;
w = distancia( x1, y1, x2, y2);
a.first = i; a.second = j;
ap.first = w;
ap.second = a;
G.push_back(ap);
narist++;
}
}

//inicializar los conjuntos
for(v = 0; v < nvert; v++)
make_set(v);
nconj = nvert;

//incluir los caminos ya construidos
scanf("%d\n",&existentes);
while(existentes){
scanf("%d %d\n", &u, &v);
//se valida antes de incluir el camino
if(find_set(u-1) != find_set(v-1)) union_set( u-1, v-1);
existentes--;
}
//ejecutar kruskal para calcular la longitud
kruskal();

//imprimir el resultado
printf("%.2llf\n", longitud);
}

return 0;
}
&istado 19 Solcin del problema 178BA
A)ora se presenta el algoritmo de <rim para encontrar el rbol
de e"pansin m$nima. A($ la diferencia con el algoritmo anterior es
(e solo se mantienen dos conjntos+ el de los %.rtices inclidos en
el rbol ' el de los (e no lo estn. !l procedimiento consiste en
elegir la arista de menor peso (e ne n %.rtice en el conjnto del
rbol con n %.rtice (e no esta en el rbol. !l sedocdigo se
presenta a continacin:
<RJ50G+ r1
1. para cada %.rtice en G
cla%eEF G infinito
padreEF G IU&O
3. cla%eErF G 7
8. 5eter los %.rtices de G a na cola de prioridad T con cla%eEF
9. 5ientras no este %ac$a T
!"traer n %.rtice de T ' llamarlo
<ara cada %.rtice % (e sea ad'acente a
o Si % esta en T ' el peso de 0+%1 U cla%eE%F
padreE%F G
cla%eE%F G S0+%1
A continacin se propone na implementacin basada en la
cola de prioridad de la S4& ' sando n algoritmo similar al de la
b#s(eda por anc)ra. &a %ariante es (e se eligen primero a(ellas
aristas con n menor peso por medio de la cola de prioridad. <ara
e%itar los ciclos+ se re%isa si el %.rtice a %isitar 'a fe inclido en el
rbol ' se marca como %isitado. <ara con%ertir la cola de prioridad
de la S4& en na cola ascendente 0el menor en el tope1 es necesario
meter los pesos como n#meros negati%os. 4ambi.n es importante
)acer notar (e n nodo no se considera %isitado )asta (e es sacado
de la cola. Una mejora simple (e se pede )acer a la
implementacin es tener n contador (e re%ise (e todos los
%.rtices feron %isitados ' )acer (e el ciclo principal termine antes.
&a implementacin del algoritmo anterior se presenta a
continacin:
#include<stdio.h>
#include<queue>
#include<vector>
#include<list>
using namespace std;
#define NVERT 9//se usa la figura 23.5 del cormen como prueba
//definicion de la arista ponderada aqui almacenamos peso, nodo destino
#define ARISTA_PONDERADA pair< int, int>
#define INFINITO 300000000
#define NULO -1
vector< list< ARISTA_PONDERADA> > G(NVERT);
int padre[NVERT], clave[NVERT];
int nvert, narist;
void prim(int r){
priority_queue< ARISTA_PONDERADA> Q;
ARISTA_PONDERADA ap;
int u, v, visitado[NVERT];
list<ARISTA_PONDERADA>::iterator aux;
//inicializar el algoritmo
for(u = 0; u < NVERT; u++){
clave[u] = INFINITO;
padre[u] = NULO;
visitado[u] = 0;
}
clave[r] = 0;
visitado[r] = 1;

//inicializar la cola de prioridad
ap.first = 0; ap.second = r;
Q.push(ap);

//ciclo principal del algoritmo
while(!Q.empty()){
ap = Q.top();//sacamos el menor elemento de la cola
Q.pop();
visitado[u] = 1;
u = ap.second;
for(aux = G[u].begin(); aux != G[u].end(); aux++){
v = (*aux).second;
if(!visitado[v] && ((*aux).first < clave[v])){
padre[v] = u;//sirve para reconstruir el arbol
clave[v] = (*aux).first;//el peso de la arista aadida
ap.first = (*aux).first*(-1);
ap.second = v;
Q.push(ap);
}
}
}
}
int main(){
int i, n;
int u, v, w;
ARISTA_PONDERADA ap;
scanf("%d %d\n", &nvert, &narist);
n = narist;
i = 0;
//ciclo para insertar las aristas en el grafo
while(n){
scanf("%d %d %d\n", &u, &v, &w);
//el grafo es no dirigido por lo se insertan en dos direcciones
ap.first = w; ap.second = v;
G[u].push_back(ap);//insertar (u, v, w)
ap.second = u;
G[v].push_back(ap);//insertar (v, u, w);
n--;
}

//se manda a llamar al metodo con la raiz en 0
prim(0);

//se imprime el arbol resultante
printf("arbol resultante\n");
for(i = 0; i < nvert; i++){
if((i != 0) && (clave[i] != INFINITO)){
u = padre[i];
v = i;
w = clave[i];
printf("(%d, %d, %d)\n", u, v, w);
}
}
return 0;
}
&istado 1: Jmplementacin de <rim
!l problema 178BA tambi.n pede resol%erse sando el
algoritmo de <rim si se ponen a cero los pesos de los caminos 'a
constridos+ esto se )ar$a desp.s del cdigo (e constr'e el grafo.
<ara ello ser$a necesario )acer b#s(edas en la lista de ad'acencias
o sar na representacin por medio de matrices de ad'acencias. &a
matri2 de ad'acencias se jstifica a($ debido a (e el grafo es m'
denso. &a solcin por este medio se deja como ejercicio. !s
tambi.n claro (e para encontrar el peso total del rbol de e"pansin
m$nima solo se tienen (e smar todas las entradas del arreglo de
cla%es.
=. Algoritmos para las rtas ms cortas
!n este apartado se re%isarn los algoritmos de las rtas ms
cortas de Dij?stra ' Flo'd+ por ser los ms conocidos ' #tiles para
resol%er los problemas de la A,5. Otros algoritmos como el de
@ellman>Ford ' el de Mars)all solo se mencionarn a manera de
sedocdigo.
4odos los algoritmos de esta seccin san la desigaldad del
tringlo+ es decir+ tratan de probar si peso0+%1 V peso0+i1 K
peso0i+%1. ,omo consecencia del )ec)o anterior+ los grafos (e
contienen ciclos negati%os no peden ser reseltos por dic)os
algoritmos al no encontrar na forma correcta de e%alar
correctamente la desigaldad.
&os algoritmos de @ellman>Ford ' de Dij?stra san los
sigientes algoritmos como iniciali2acin ' para determinar na
mejor rta. Dic)os algoritmos se listan a continacin a manera de
sedocdigo:
Jniciali2acin0G+ s1
1. para cada %.rtice % en G
dE%F G JIFJIJ4O
padreE%F G IU&O
3. dEsF G 7
Relajamiento0+%1
1. si dE%F V dEF K peso0+%1
dE%F G dEF K peso0+%1
padreE%F G
A continacin se mestra el algoritmo de @ellman>Ford+ este
algoritmo tiene la particlaridad de (e es capa2 de detectar si
e"isten ciclos negati%os en el grafo. <or consecencia sige
fncionando a pesar de encontrar aristas con pesos negati%os con la
condicin de (e no e"istan los mencionados ciclos negati%os.
@!&&5AI>FORD0G+s1
1. Jniciali2acion0G+s1
3. para i G 1 )asta n%ert W 1
para cada arista 0+%1 en G relajamiento0+%1
8. para cada arista 0+%1 en G
si dEF V dEF K peso0+%1 retrn FA&SO
9. retrn /!RDAD!RO
!l ciclo interior del paso 3 ' el paso 8 peden )acerse con
a'da de na lista de aristas. Al terminar de ejectarse el algoritmo+
las distancias ms cortas sern almacenadas en el arreglo d ' los
caminos de s al resto de los %.rtices pede encontrarse por medio del
algoritmo recrsi%o de caminos (e se estdio en el apartado 8
OAlgoritmos bsicos de b#s(edaP ' (e se transcribe a
continacin a manera de sedocdigo:
,amino0+%1
1. si G % imprime
3. en caso contrario si padreE%F G IU&O
no e"iste camino de a %
8. si padreE%F es diferente de IU&O
camino0+ padreE%F1
imprime %
Debido a (e en el paso 3 el algoritmo de @ellman>Ford es
lento+ s so se restringe a a(ellos casos en los (e es importante
identificar si e"isten ciclos negati%os en el grafo. !n caso de
encontrar n ciclo negati%o el algoritmo de%el%e FA&SO. &a
implementacin del algoritmo de @ellman>Ford se deja como
ejercicio.
A continacin se presenta el sedocdigo de n algoritmo
eficiente para trabajar con grafos dirigidos ac$clicos o dagXs. !ste
algoritmo apro%ec)a (e no e"isten ciclos en el grafo para pro%eer
de n algoritmo de eficiencia lineal. Se )ace so del ordenamiento
topolgico como parte del preprocesamiento del grafo. A
continacin se presenta el sedocdigo de las rtas ms cortas en
n dag.
Rtas>cortas>dags0G+ s1
1. Ordenar topolgicamente G
3. Jniciali2acin0G+s1
8. para cada %.rtice + ordenado topolgicamente
para cada %.rtice % (e es ad'acente a Relajamiento0+%1
Una aplicacin importante del algoritmo anterior es para
constrir el anlisis temporal de pro'ectos sando <!R4. &a rta
ms larga ofrecida por el algoritmo anterior corresponde a la rta
cr$tica (e trata de redcirse sando <!R4.
A)ora es el momento de anali2ar con detalle no de los
algoritmos clsicos para encontrar las rtas ms cortas. Se trata del
algoritmo de Dij?stra+ el cal por medio de na t.cnica %ida
actali2a n %ector de padres ' de no de distancias m$nimas. &as
rtas peden encontrarse con el algoritmo recrsi%o de caminos (e
se describi en prrafos anteriores. A continacin se lista el
sedocdigo del algoritmo de Dij?stra.
DJYRS4RA0G+s1
1. Jniciali2acin0G+s1
3. S es el conjnto %ac$o
8. 5eter los %.rtices a na cola de prioridad T de acerdo a dEF
9. mientras T no este %ac$a
e"traer el minimo de T en
S G S nion Z[
para cada % ad'acente a relajamiento0+%1
&a implementacin del algoritmo de Dij?stra es m' similar a
la del algoritmo de <rim. !l conjnto S representa a los %.rtices 'a
%isitados por el algoritmo ' (e por tanto no sern inclidos en la
cola de prioridad. !l procedimiento de relajamiento deber sin
embargo actali2ar las distancias de todos los nodos ad'acentes a +
sin importar si feron o no %isitados con anterioridad. !l algoritmo
de Dij?stra no fnciona para grafos con aristas negati%as sin
importar si e"isten o no ciclos negati%os. A continacin se presenta
na implementacin basada en las colas de prioridad de la S4&+
ntese (e es casi id.ntica a la implementacin de <rim.
#include<stdio.h>
#include<queue>
#include<vector>
#include<list>
using namespace std;
#define NVERT 9
//definicion de la arista ponderada aqu almacenamos peso, nodo destino
#define ARISTA_PONDERADA pair< int, int>
#define INFINITO 300000000
#define NULO -1
vector< list< ARISTA_PONDERADA> > G(NVERT);
int padre[NVERT], d[NVERT];
int nvert, narist;
//implementacin del algoritmo de dijkstra
void dijkstra(int s){//nodo de origen s
priority_queue< ARISTA_PONDERADA> Q;
ARISTA_PONDERADA ap;
int u, v, visitado[NVERT];
list<ARISTA_PONDERADA>::iterator aux;
//inicializar el algoritmo
for(u = 0; u < NVERT; u++){
d[u] = INFINITO;
padre[u] = NULO;
visitado[u] = 0;
}
d[s] = 0;
visitado[s] = 1;

//inicializar la cola de prioridad
ap.first = 0; ap.second = s;
Q.push(ap);


//ciclo principal del algoritmo
while(!Q.empty()){
ap = Q.top();//sacamos el menor elemento de la cola
Q.pop();
u = ap.second;//recuperamos vertice u
visitado[u] = 1;//aadir u a visitados
//tomar los vertices adyacentes a u para hacer el relajamiento
for(aux = G[u].begin(); aux != G[u].end(); aux++){
v = (*aux).second;
if( d[v] > (d[u] + (*aux).first) ){//relajamiento
padre[v] = u;//sirve para reconstruir el arbol
d[v] = d[u] + (*aux).first;//actualizar la ruta ms corta
//meter a la cola solo las distancias de vertices no visitados
if(!visitado[v]){
ap.first = d[v]*(-1);//cambiamos a cola ascendente
ap.second = v;
Q.push(ap);
}//fin de meter a la cola
}//fin del relajamiento
}//fin del for
}//fin del while
}//fin de dijkstra
//programa de prueba para el algoritmo de dijkstra
int main(){
int i, n;
int u, v, w;
ARISTA_PONDERADA ap;
scanf("%d %d\n", &nvert, &narist);
n = narist;
i = 0;
//ciclo para insertar las aristas en el grafo
while(n){
scanf("%d %d %d\n", &u, &v, &w);
//el grafo es dirigido por lo se inserta solo en una direccin
ap.first = w; ap.second = v;
G[u].push_back(ap);//insertar (u, v, w)
n--;
}

//se manda a llamar al metodo con la raiz en 0
dijkstra(0);

//se imprime las aristas del arbol resultante
printf("aristas del arbol resultante\n");
for(i = 0; i < nvert; i++){
if((i != 0) && (d[i] != INFINITO)){
u = padre[i];
v = i;
printf("(%d, %d)\n", u, v);
}
}
//se imprime el vector de distancias minimas a partir de 0
printf("Distancias minimas a partir del nodo 0\n");
for(i = 0; i < nvert; i++)
printf("d[%d] = %d\n", i, d[i]);
return 0;
}
&istado 1= Jmplementacin de Dij?stra
!l algoritmo anterior se prob con el grafo de la figra 39.= del
cormen. Una %e2 ms se recerda (e por medio del algoritmo
recrsi%o camino0+ %1 peden reconstrirse las rtas encontradas
por Dij?stra+ siempre (e sea el nodo de origen para el algoritmo.
A)ora se mestra la solcin del problema 171A1 O5eeting
<rof. 5igelP del je2 de la U/A+ sando el algoritmo de Dij?stra.
!n este caso se sa para encontrar los %ectores de las rtas ms
cortas en el grafo de las personas ma'ores ' lego las rtas ms
cortas en el grafo de las personas menores. Al final se sman las
distancias encontradas ' se eligen a(ellas posiciones (e tienen al
menor. <ara poder sar el mismo cdigo fe necesario sar
parmetros para la fncin dijs?tra+ ntese (e los grafos se pasan
por referencia para no generar copias. !l cdigo de la solcin
aceptada se mestra a continacin.
#include <stdio.h>
#include <queue>
#include <vector>
#include <list>
using namespace std;
#define NVERT 26//todas las letras del abecedario
//definicion de la arista ponderada aqui almacenamos peso, nodo destino
#define ARISTA_PONDERADA pair< int, int>
#define INFINITO 300000000
#define NULO -1
//se almacenan los dos grafos, calles para menores y mayores
vector< list< ARISTA_PONDERADA> > GMayores(NVERT), GMenores(NVERT);
int padre[NVERT], dMayores[NVERT], dMenores[NVERT];
//al dijkstra se le pasa el origen, el grafo y el vector de distancias
//si fuera necesario se le pasaria el vector de padres
void dijkstra(int s, vector< list<ARISTA_PONDERADA> > &G, int d[]){
priority_queue< ARISTA_PONDERADA> Q;
ARISTA_PONDERADA ap;
int u, v, visitado[NVERT];
list<ARISTA_PONDERADA>::iterator aux;
//inicializar el algoritmo
for(u = 0; u < NVERT; u++){
d[u] = INFINITO;
padre[u] = NULO;
visitado[u] = 0;
}
d[s] = 0;
visitado[s] = 1;

//inicializar la cola de prioridad
ap.first = 0; ap.second = s;
Q.push(ap);

//ciclo principal del algoritmo
while(!Q.empty()){
ap = Q.top();//sacamos el menor elemento de la cola
Q.pop();
u = ap.second;
visitado[u] = 1;//aadir u a visitados
//tomar los vertices de la cola los vertices no visitados
for(aux = G[u].begin(); aux != G[u].end(); aux++){
v = (*aux).second;
if( d[v] > (d[u] + (*aux).first) ){//relajamiento
padre[v] = u;//sirve para reconstruir el arbol
d[v] = d[u] + (*aux).first;//actualizar la ruta ms corta
//meter a la cola solo las distancias de vertices no visitados
if(!visitado[v]){
ap.first = (*aux).first*(-1);//cambiamos a cola ascendente
ap.second = v;
Q.push(ap);
}
}
}
}
}
int main(){
ARISTA_PONDERADA ap;
int num_calles, n;
int minimos[NVERT];
char usuario, dir, ciudad1, ciudad2, sha, mig;
int peso;
int min, i;
while(1){

//leer datos del caso
scanf("%d\n", &num_calles);
//termina con un cero
if(!num_calles) break;

//se limpia la informacin del caso anterior
for(i = 0; i < 26; i++){
GMenores[i].clear();
GMayores[i].clear();
}


//ciclo para leer las aristas
n = num_calles;
while(n>0){
//leer arista
scanf("%c %c %c %c %d\n", &usuario, &dir, &ciudad1, &ciudad2, &peso);

//validar caso especial
if(ciudad1 == ciudad2) peso = 0;

ap.first = peso;

if(usuario == 'Y'){
ap.second = ciudad2 - 'A';
GMenores[ciudad1 - 'A'].push_back(ap);
if(dir == 'B'){
ap.second = ciudad1 - 'A';
GMenores[ciudad2 - 'A'].push_back(ap);
}
}else{
ap.second = ciudad2 - 'A';
GMayores[ciudad1 - 'A'].push_back(ap);
if(dir == 'B'){
ap.second = ciudad1 - 'A';
GMayores[ciudad2 - 'A'].push_back(ap);
}
}
n--;
}

//leer consulta
scanf("%c %c\n", &sha, &mig);

dijkstra( sha - 'A', GMenores, dMayores);
dijkstra( mig - 'A', GMayores, dMenores);

min = dMayores[0] + dMenores[0];
minimos[0] = min;

for(i = 1; i < 26; i++)
if((dMayores[i] + dMenores[i]) <= min){
min = dMayores[i] + dMenores[i];
minimos[i] = min;
}else{
minimos[i] = 15000;
}

if(min >= 15000)
printf("You will never meet.\n");
else{
printf("%d", min);
for( i = 0; i < 26; i++)
if(minimos[i] == min) printf(" %c", i + 'A');
printf("\n");
}
}

return 0;
}
&istado 1A Solcin del problema 171A1
&a solcin del listado anterior pede mejorarse en tiempo si
)acemos (e el ciclo principal de Dij?stra termine cando todos los
nodos est.n marcados como %isitados+ para ello basta con lle%ar n
contador ' encontrar la forma de contar el n#mero de %.rtices. !n el
caso de este problema se podr$a lle%ar n arreglo de banderas (e se
ponga a 1 cada %e2 (e n %.rtice se a6ade al grafo ' lego se
contar$an los 1\s para saber cantos %.rtices e"isten en el grafo. &as
banderas tendr$an (e iniciali2arse a 7. !l tiempo a#n sin la mejora
(e se menciona es bastante beno 07.773 segndos1 por lo (e en
este caso no %ale la pena+ sin embargo pede ser #til al resol%er n
problema con casos ms grandes.
A)ora se presentan los algoritmos (e encentran las
distancias ms cortas entre todos los pares de %.rtices. A#n cando
esto pede consegirse con n ciclo (e %ar$e el %.rtice de origen en
Dijs?tra+ por lo general se sa el algoritmo de Flo'd por tener na
implementacin ms simple como se %er a continacin.
!l algoritmo basa s fncionamiento en na t.cnica de
programacin dinmica (e almacena al paso de cada iteracin el
mejor camino entre el (e pasa por el nodo intermedio ? ' el (e %a
directamente del nodo i al nodo j. <ara determinar cal es el mejor
camino se sige sando la desigaldad del tringlo ' para (e el
algoritmo fncione es necesario )acer na iniciali2acin con los
sigientes %alores:
7 si i G j
M
ij
G el peso de la arista dirigida 0i+j1 si i

j ' 0i+j1

si i

j ' 0i+j1
!l algoritmo permite la presencia de aristas negati%as siempre
(e no e"istan ciclos negati%os como scede con el algoritmo de
@ellman>Ford. !l m.todo consiste en )acer iteraciones del proceso
de relajacin para cada arista en el grafo+ tomando en cada paso n
%.rtice intermedio diferente. &a manera ms simple de
implementarlo es por medio de tres ciclos anidados (e iteran sobre
n grafo representado por na matri2 de ad'acencia. Al igal (e los
otros m.todos presentados con anterioridad es posible reconstrir el
camino ms corto entre cal(ier par de nodos por medio de n
procedimiento recrsi%o similar al presentado anteriormente. <ara
reconstrir el camino es necesario tener na matri2 de padres (e se
actali2a drante el proceso de relajacin. !l algoritmo de Flo'd se
presenta a continacin a manera de sedocdigo:
Flo'd>Mars)all
1. n G nmero de %.rtices
3. para ? G 1 to n
8. para i G 1 to n
9. para j G 1 to n
si S0i+j1 V S0i+?1 K S0?+j1
o S0i+j1 G S0i+?1 K S0?+j1
o padre0i+j1 G ?
,omo pede apreciarse del sedocdigo la implementacin es
directa+ a continacin se mestra la implementacin del algoritmo
para la iniciali2acin+ insertar na arista+ encontrar las rtas ms
cortas ' para recperar el camino.
//definiciones para el algoritmo
#define INFINITO 10000000
#define NULO -1
//matrices de pesos y de padres
int W[NVERT][NVERT];
int Padre[NVERT][NVERT];
//inicializar la matriz de adyacencia y de padres
void inicializar(){
int i, j;
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++){
Padre[i][j] = NULO;
if(i == j) W[i][j] = 0;
else W[i][j] = INFINITO;
}
}
//insertar una arista validando i = j
void inserta_arista(int i, int j, int w){
if(i == j) W[i][j] = 0;
else W[i][j] = w;
}
//validacion para suma con infinito
int suma(int x, int y){
if( x == INFINITO || y == INFINITO)
return INFINITO;
else
return x + y;
}
//algoritmo que calcula las rutas ms cortas
void floyd(){
int i, j, k;

//ciclo principal de floyd
for(k = 0; k < NVERT; k++)
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++)
if( W[i][j] > suma( W[i][k], W[k][j]) ){
W[i][j]=suma( W[i][k], W[k][j]);
Padre[i][j] = k;
}
}
//algoritmo para imprimir la ruta mas corta
void camino(int origen, int destino){
if( origen == destino){//caso base
printf("%d", origen);
}else{
if( Padre[origen][destino] == NULO ){//no existe camino
printf("No existe camino de %d a %d", origen, destino);
}else{
camino(origen, Padre[origen][destino]);//llamada recursiva
printf("%d", destino);//imprimir en orden origen, destino
}
}
}
&istado 1C Jmplementacin de Flo'd
,omo pede %erse la implementacin del algoritmo es bastante
simple ' es por ello (e se prefiere sobre Dij?stra cando el tama6o
del problema es redcido 0I/!R4 U 3771. &a ra2n para no sar
siempre Flo'd es (e s eficiencia es c#bica en el nmero de
%.rtices como pede dedcirse fcilmente de s implementacin. !l
resltado de la rta ms corta entre i ' j se encentra en MEiFEjF si es
(e e"iste dic)a rta+ en caso contrario la rta tendr n %alor de
infinito. Algo similar reportar$a el algoritmo para imprimir la rta+ al
encontrar n padre con %alor a nlo en s proceso. !s importante
)acer notar (e la parte de iniciali2acin es cla%e para encontrar los
resltados correctos ' debe )acerse antes de ejectar el algoritmo '
de ingresar las aristas a la matri2. Otra obser%acin importante para
lograr na implementacin e"itosa es el )acer las smas con infinito
de manera correcta.
A continacin se presenta la solcin del problema 171A1
sando el algoritmo de Flo'd. A($ se sa Flo'd dos %eces+ na
sobre la matri2 de las calles para ma'ores ' otra para la de menores.
&a solcin es m' similar a la presentada con el algoritmo de
Dij?stra+ an(e como es de esperarse el tiempo de ejeccin es
ma'or.
#include <stdio.h>
//matrices de pesos
int grafo_mayores[30][30], grafo_menores[30][30];
//variables a usar en el algoritmo
int num_calles, n;
int minimos[30];
char usuario, dir, ciudad1, ciudad2, sha, mig;
int peso;
int i, j , k;
int min, pos_min;
int main(){
//ciclo principal del problema
while(1){

//leer el numero de calles en el grafo
scanf("%d\n", &num_calles);
if(!num_calles) break;//terminar con cero
//inicializar las matrices
for(i = 0; i < 26; i++){
for(j = 0; j < 26; j++)
grafo_mayores[i][j] = grafo_menores[i][j] = 15000;
grafo_mayores[i][i] = grafo_menores[i][i] = 0;
}

//leer las aristas e insertarlas en los grafos
n = num_calles;
while(n>0){
scanf("%c %c %c %c %d\n", &usuario, &dir, &ciudad1, &ciudad2, &peso);
if(ciudad1 == ciudad2) peso = 0;//validacin i = j
if(usuario == 'Y'){
grafo_menores[ciudad1-'A'][ciudad2-'A'] = peso;
if(dir == 'B')
grafo_menores[ciudad2-'A'][ciudad1-'A'] = peso;
}else{
grafo_mayores[ciudad1-'A'][ciudad2-'A'] = peso;
if(dir == 'B')
grafo_mayores[ciudad2-'A'][ciudad1-'A'] = peso;
}
n--;
}
//floyd sobre el grafo de mayores
for(k = 0; k < 26; k++)
for(i = 0; i < 26; i++)
for(j = 0; j < 26; j++)
if(grafo_mayores[i][j]>(grafo_mayores[i][k]+grafo_mayores[k][j]))
grafo_mayores[i][j]=grafo_mayores[i][k]+grafo_mayores[k][j];

//floyd sobre el grafo de menores
for(k = 0; k < 26; k++)
for(i = 0; i < 26; i++)
for(j = 0; j < 26; j++)
if(grafo_menores[i][j]>(grafo_menores[i][k]+grafo_menores[k][j]))
grafo_menores[i][j]=grafo_menores[i][k]+grafo_menores[k][j];

//leer consulta
scanf("%c %c\n", &sha, &mig);
//encontrar el menor esfuerzo combinado
min = grafo_mayores[mig-'A'][0] + grafo_menores[sha-'A'][0];
minimos[0] = min;
for(i = 1; i < 26; i++)
if((grafo_mayores[mig-'A'][i] + grafo_menores[sha-'A'][i]) <= min){
min = grafo_mayores[mig-'A'][i] + grafo_menores[sha-'A'][i];
minimos[i] = min;
}else{
minimos[i] = 15000;
}
//imprimir los resultados
if(min >= 15000)
printf("You will never meet.\n");
else{
printf("%d", min);
for( i = 0; i < 26; i++)
if(minimos[i] == min) printf(" %c", i + 'A');
printf("\n");
}
}
return 0;
}
&istado 1B Solcin del problema 171A1 con Flo'd
!l algoritmo de Flo'd es m' %erstil a pesar de ser m'
simple ' se sa para encontrar la solcin de problemas donde es
necesario encontrar el mejor camino basado en restricciones de
carga m"ima o de esfer2o m$nimo. !n esos casos es necesario
modificar el proceso de relajacin para sstitirlo por no (e elija
en cada iteracin la mejor solcin en fncin de las restricciones. A
continacin se presentan los algoritmos llamados ma"min '
minma" constridos sobre na modificacin a Flo'd.
<rimero re%isaremos el problema de encontrar la carga m"ima
(e es posible transportar en na rta determinada+ cando en cada
segmento de la rta e"iste na restriccin del m"imo (e se pede
transportar por dic)o segmento. Anali2ando con calma el problema
es fcil dedcir (e la m"ima carga a transportar por el camino %1+
%3+ ]+ %n corresponde al m$nimo0 S0%1+ %31+ S0%3+ %81+ S0%n>1+
%n11 ' la carga m"ima (e se pede lle%ar del nodo %1 al nodo %n
corresponde al m"imo entre las distintas rtas (e peden formarse
en el grafo. De este modo tenemos n problema del tipo ma"min+
donde las aristas (e no estn conectadas en el grafo debern
iniciali2arse con cero indicando (e por ellas se pede transportar
na carga de cero. &a implementacin de ma"min ser$a como se
mestra a continacin:
//definiciones para el algoritmo
#define INFINITO 10000000
#define NULO -1
int W[NVERT][NVERT];
int Padre[NVERT][NVERT];
//inicializar la matriz de adyacencia y de padres
void inicializar(){
int i, j;
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++){
Padre[i][j] = NULO;
W[i][j] = 0;
}
}
//insertar una arista validando i = j
void inserta_arista(int i, int j, int w){
if(i == j) W[i][j] = 0;
else W[i][j] = w;
}
//funciones de soporte para determinar los maximos y minimos
int max(int x, int y){
if( x > y) return x;
else return y;
}
int min(int x, int y){
if( x < y) return x;
else return y;
}
//algoritmo que calcula la carga maxima
void maxmin(){
int i, j, k;

//ciclo principal de floyd
for(k = 0; k < NVERT; k++)
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++)
if( W[i][j] < min( W[i][k], W[k][j]) ){
W[i][j] = min( W[i][k], W[k][j]);
Padre[i][j] = k;
}
//tambien se puede sustituir el if por
//W[i][j] = max( W[i][j], min( W[i][k], W[k][j]));
}
//algoritmo para imprimir la ruta de la carga mxima
void camino(int origen, int destino){
if( origen == destino){//caso base
printf("%d", origen);
}else{
if( Padre[origen][destino] == NULO ){//no existe camino
printf("No existe camino de %d a %d", origen, destino);
}else{
camino(origen, Padre[origen][destino]);//llamada recursiva
printf("%d", destino);//imprimir en orden origen, destino
}
}
}
&istado 37 Jmplementacin de ma"min
A continacin se mestra la solcin del problema 177BB
O4)e 4orist GideP del je2 de la U/A+ donde se sa el algoritmo
anterior. A($ el detalle consiste en considerar (e el n#mero
m"imo de pasajeros dismin'e en no por el lgar (e debe ocpar
el g$a. &a solcin aceptada por el je2 es la sigiente:
#include <stdio.h>
using namespace std;
//definiciones para el algoritmo
#define NULO -1
#define MAXVERT 100
int NVERT;
int W[MAXVERT][MAXVERT];
int Padre[MAXVERT][MAXVERT];
//inicializar la matriz de adyacencia y de padres
void inicializar(){
int i, j;
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++){
Padre[i][j] = NULO;
W[i][j] = 0;
}
}
//insertar una arista validando i = j
void inserta_arista(int i, int j, int w){
if(i == j) W[i][j] = 0;
else W[i][j] = w;
}
//funciones de soporte para determinar los maximos y minimos
int max(int x, int y){
if( x > y) return x;
else return y;
}
int min(int x, int y){
if( x < y) return x;
else return y;
}
//algoritmo que calcula la carga maxima
void maxmin(){
int i, j, k;

//ciclo principal de floyd
for(k = 0; k < NVERT; k++)
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++)
if( W[i][j] < min( W[i][k], W[k][j]) ){
W[i][j] = min( W[i][k], W[k][j]);
Padre[i][j] = k;
}
//tambien se puede sustituir el if por
//W[i][j] = max( W[i][j], min( W[i][k], W[k][j]));
}
//algoritmo para imprimir la ruta de la carga mxima
void camino(int origen, int destino){
if( origen == destino){//caso base
printf("%d", origen);
}else{
if( Padre[origen][destino] == NULO ){//no existe camino
printf("No existe camino de %d a %d", origen, destino);
}else{
camino(origen, Padre[origen][destino]);//llamada recursiva
printf("%d", destino);//imprimir en orden origen, destino
}
}
}
int main(){
int N, R;
int C1, C2, P;
int S, D, T;
int caso, i;
int maximo, num_viajes;
caso = 1;
while(1){
scanf("%d %d\n", &N, &R);
if(!N && !R) break;
NVERT = N;
inicializar();
for(i = 0; i < R; i++){
scanf("%d %d %d\n", &C1, &C2, &P);
inserta_arista( C1 - 1, C2 - 1, P);
inserta_arista( C2 - 1, C1 - 1, P);
}

//ejecutar el algoritmo
maxmin();

//mostrar los resultados
scanf("%d %d %d\n", &S, &D, &T);
printf("Scenario #%d\n", caso++);
//restar lugar del gua de turistas
maximo = W[S-1][D-1] - 1;
//calcular el numero mximo de viajes
num_viajes = T / maximo;
if( (T % maximo) != 0 ) num_viajes++;
printf("Minimum Number of Trips = %d\n", num_viajes);
printf("\n");
}
}
&istado 31 Solcin del problema 177BB
A)ora pasamos a anali2ar el problema contrario+ spongamos
(e deseamos minimi2ar el esfer2o necesario para completar na
tarea a partir de n conjnto de restricciones (e nos imponen el
esfer2o re(erido en cada etapa. Si anali2amos el problema nos
damos centa (e en la secencia %1+ %3+ ]+ %n el esfer2o m"imo
(e debemos reali2ar corresponde a ma"0 S0%1+ %31+ S0%3+ %81+ ]+
S0%n>1+ %n11. !l problema se resel%e eligiendo la rta o secencia
(e minimice dic)a cantidad. A este problema se le conoce como
distancia m$nima ' para ponerlo a fncionar es necesario (e todas
aristas no conectadas en el grafo debern iniciali2arse con infinito+
indicando (e para ellas se re(iere n esfer2o m' grande (e no
ser elegido drante las iteraciones. A continacin se presenta la
implementacin de dic)o algoritmo:
//definiciones para el algoritmo
#define INFINITO 10000000
#define NULO -1
int W[NVERT][NVERT];
int Padre[NVERT][NVERT];
//inicializar la matriz de adyacencia y de padres
void inicializar(){
int i, j;
for(i = 0; i < NVERT; i++){
for(j = 0; j < NVERT; j++){
Padre[i][j] = NULO;
W[i][j] = INFINITO;
}
W[i][i] = 0;
}
}
//insertar una arista validando i = j
void inserta_arista(int i, int j, int w){
if(i == j) W[i][j] = 0;
else W[i][j] = w;
}
//funciones de soporte para determinar los maximos y minimos
int max(int x, int y){
if( x > y) return x;
else return y;
}
int min(int x, int y){
if( x < y) return x;
else return y;
}
//algoritmo que calcula la carga minima
void minmax(){
int i, j, k;

//ciclo principal de floyd
for(k = 0; k < NVERT; k++)
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++)
if( W[i][j] > max( W[i][k], W[k][j]) ){
W[i][j] = max( W[i][k], W[k][j]);
Padre[i][j] = k;
}
//tambien se puede sustituir el if por
//W[j] = min( W[i][j], max( W[i][k], W[k][j]));
}
//algoritmo para imprimir la ruta de la carga mxima
void camino(int origen, int destino){
if( origen == destino){//caso base
printf("%d", origen);
}else{
if( Padre[origen][destino] == NULO ){//no existe camino
printf("No existe camino de %d a %d", origen, destino);
}else{
camino(origen, Padre[origen][destino]);//llamada recursiva
printf("%d", destino);//imprimir en orden origen, destino
}
}
}
&istado 33 Jmplementacin de minima"
Itese (e la implementacin de minma" es tambi.n m'
simple ' solo cambia el )ec)o de cmo se interpretan las aristas no
conectadas en la matri2 de pesos. !n este caso las aristas 0i+i1 sigen
siendo cero ' las 0i+j1 (e no pertenecen al conjnto de aristas %alen
infinito. !n segida se mestra la solcin del problema 1779C
OAdiop)obiaP de la U/A+ sando el algoritmo anterior.
#include <stdio.h>
using namespace std;
#define MAXVERT 100
//definiciones para el algoritmo
#define INFINITO 10000000
#define NULO -1
int NVERT;
int W[MAXVERT][MAXVERT];
int Padre[MAXVERT][MAXVERT];
//inicializar la matriz de adyacencia y de padres
void inicializar(){
int i, j;
for(i = 0; i < NVERT; i++){
for(j = 0; j < NVERT; j++){
Padre[i][j] = NULO;
W[i][j] = INFINITO;
}
W[i][i] = 0;
}
}
//insertar una arista validando i = j
void inserta_arista(int i, int j, int w){
if(i == j) W[i][j] = 0;
else W[i][j] = w;
}
//funciones de soporte para determinar los maximos y minimos
int max(int x, int y){
if( x > y) return x;
else return y;
}
int min(int x, int y){
if( x < y) return x;
else return y;
}
//algoritmo que calcula la carga minima
void minmax(){
int i, j, k;

//ciclo principal de floyd
for(k = 0; k < NVERT; k++)
for(i = 0; i < NVERT; i++)
for(j = 0; j < NVERT; j++)
if( W[i][j] > max( W[i][k], W[k][j]) ){
W[i][j] = max( W[i][k], W[k][j]);
Padre[i][j] = k;
}
//tambien se puede sustituir el if por
//W[j] = min( W[i][j], max( W[i][k], W[k][j]));
}
//algoritmo para imprimir la ruta de la carga mxima
void camino(int origen, int destino){
if( origen == destino){//caso base
printf("%d", origen);
}else{
if( Padre[origen][destino] == NULO ){//no existe camino
printf("No existe camino de %d a %d", origen, destino);
}else{
camino(origen, Padre[origen][destino]);//llamada recursiva
printf("%d", destino);//imprimir en orden origen, destino
}
}
}
int main(){
int caso;
int S, C, Q;
int c1, c2, d;
int minimo;
caso = 1;
while(1){

scanf("%d %d %d\n", &C, &S, &Q);
if(!C && !S && !Q) break;
if(caso>1) printf("\n");

//leer las aristas del grafo
NVERT = C;
inicializar();
while(S){
scanf("%d %d %d\n", &c1, &c2, &d);
inserta_arista(c1 - 1, c2 - 1, d);
inserta_arista(c2 - 1, c1 - 1, d);
S--;
}

//encontrar los minimos
minmax();

//imprimir los resultados
printf("Case #%d\n", caso++);

//leer las consultas
while(Q){
//leer la consulta
scanf("%d %d\n", &c1, &c2);
minimo = W[c1 - 1][ c2 - 1];

//validar la salida
if( minimo == INFINITO) printf("no path\n");
else printf("%d\n", minimo);
Q--;
}
}
}
&istado 38 Solcin del problema 1779C
,omo complemento al presente material les recomiendo leer la
pgina met)ods to sol%e de Ste%en Nalim de la IUS. !n los
cap$tlos del libro de ,ormen peden encontrar material adicional
acerca de las aplicaciones de los algoritmos de caminos ms cortos.
!n especial reslta interesante el calclar la cerradra transiti%a de la
matri2 de ad'acencias sando Flo'd. As$ mismo se mestra la
relacin entre el prodcto de matrices ' el algoritmo de Flo'd. Otro
algoritmo interesante basado en na combinacin de @ellman>Ford '
dij?stra se mestra como algoritmo de Yo)nson.
A. Algoritmos de Fljos
&os algoritmos de fljos resel%en el problema de encontrar el
fljo m"imo de na fente a n smidero respetando na serie de
restricciones. &a primera de ellas (e el fljo los fljos se miden
como el fljo (e sale de n nodo+ si as$ ocrriera el fljo se
considera positi%o+ en caso contrario tenemos n fljo negati%o. De
esta forma+ si el fljo de i a j es positi%o entonces el fljo de j a i es
negati%o. &a fente tiene n fljo neto positi%o+ el smidero tiene n
fljo neto negati%o ' los nodos intermedios en los caminos (e %an
de la fente al smidero tienen n fljo neto igal a cero. A esta
propiedad se le conoce como conser%acin del fljo ' es el
e(i%alente a las le'es de conser%acin de la materia en f$sica '
le'es de Rirc)off en electricidad. As$ el fljo neto (e sale de la
fente es igal al fljo (e entra al smidero. <ara todos los dems
nodos el fljo neto debe ser cero+ entendiendo como fljo neto a la
sma de todos los fljos (e entran ' salen de n nodo. <or ltimo+
ning#n fljo debe sobrepasar la capacidad m"ima indicada para
cada arista en el grafo (e representa la red de nodos.
@asado en las propiedades ' restricciones anteriores se
desarrollo el algoritmo de Ford>Fl?erson c'o sedocdigo se
presenta a continacin:
FORD>FU&R!RSOI0 f+ s1
1. <ara cada arista 0+ %1 en el grafo
fEFE%F G 7
fE%FEF G 7
3. 5ientras e"ista n camino de fljo residal entre f ' s
incremento G min0cap0+%1 tal (e 0+%1 est en el camino1
para cada arista 0+%1 en el camino
o fEFE%F G fEFE%F K incremento
o fE%FEF G >fEFE%F
<ara comprender mejor el algoritmo anterior es necesario
definir algnos conceptos. <rimero decimos (e n grafo (e
representa fljos es n grafo dirigido ' ponderado+ donde el peso de
las aristas representa na capacidad m"ima de transportar n fljo.
!l fljo residal es el fljo disponible en na determinada arista na
%e2 (e se )a en%iado fljo por ella 0en ning#n caso el fljo neto
residal debe ser ma'or a la capacidad de dic)a arista ni menor (e
cero1. !l fljo residal lo calclamos como la capacidad W
fljoDactal+ donde fljoDactal es el fljo (e 'a se )a ocpado en
algna iteracin del algoritmo. Un camino de fljo residal es a(el
camino de la fente al smidero donde todas las aristas en el camino
tienen n fljo residal ma'or a cero.
!l algoritmo comien2a por )acer (e el fljo actal en todas
las aristas del grafo sea igal a cero+ en consecencia el fljo
residal ser igal a la capacidad de las mismas. !l sigiente paso es
encontrar n camino de la fente al smidero donde todas las aristas
inclidas en el camino tengan na capacidad residal ma'or a cero.
&a cantidad m"ima de fljo (e pede en%iarse al smidero por
dic)o camino corresponde como es lgico al %alor de la capacidad
residal m$nima en dic)o camino. A esta cantidad se le denomina
incremento en el fljo+ debido a (e se sma al fljo actal en todas
las aristas en el camino encontrado. &a consecencia inmediata es
(e el fljo residal se %er modificado ' la arista con la menor
capacidad estar transportando el fljo m"imo 0s fljo residal se
con%ertir en cero1 ' por lo tanto no deber ser considerada en la
sigiente iteracin del algoritmo. !ste proceso se repite siempre (e
peda encontrarse n ne%o camino de fljo residal 0n camino
donde todas las aristas tengan n fljo residal ma'or a cero1. Al
final el fljo m"imo (e pede en%iarse de la fente al smidero
corresponde a la sma de todos los incrementos calclados con cada
ne%o camino encontrado.
!l algoritmo de Ford>Fl?erson %depende fertemente del
m.todo (e se se para encontrar los caminos de fljo residal '
estos a s %e2 dependen de la forma en la (e se represente el grafo.
<or n lado+ la representacin de matrices )ace m' rpido el
encontrar el %alor de los fljos ' las capacidades de cada arista pero
)ace lento el encontrar los nodos ad'acentes ' por lo tanto la
b#s(eda de caminos. <or otro lado+ las listas de ad'acencias )acen
m' rpido el encontrar los nodos ad'acentes pero )acen lento el
encontrar el %alor de los fljos ' capacidades. A continacin se
presenta na implementacin basada en matrices de ad'acencia en
donde se apro%ec)a la simplicidad del manejo de dic)a estrctra de
datos.
#include <stdio.h>
#include <list>
using namespace std;
//definiciones para el algoritmo
#define MAXVERT 100
#define NULO -1
#define INFINITO 100000000
//definicin de una estructura para almacenar los flujos actuales y capacidades
typedef struct{
int flujo;
int capacidad;
}FLUJOS;
//el grafo se almacena como una matriz
FLUJOS grafo[MAXVERT][MAXVERT];
int nvert, padre[MAXVERT];
//valores iniciales de los flujos antes de insertar aristas
void inicia_grafo(){
int i, j;
for(i = 0; i < nvert; i++)
for(j = 0; j < nvert; j++)
grafo[i][j].capacidad = 0;
}
//se considera que puede haber mas de una arista entre cada para de vertices
void inserta_arista(int origen, int destino, int capacidad){
grafo[origen][destino].capacidad += capacidad;
}
//busqueda de caminos residuales, devuelve verdadero al encontrar un camino
int BFS(int fuente, int sumidero){
int visitado[MAXVERT], u, v, residual;
list<int> cola;

//inicializar la busqueda
for(u = 0; u < nvert; u++){
padre[u] = NULO;
visitado[u] = 0;
}
cola.clear();

//hacer la busqueda
visitado[fuente] = 1;
cola.push_back(fuente);
//ciclo principal de la busqueda por anchura
while(!cola.empty()){
//saca nodo de la cola
u = cola.front(); cola.pop_front();
for(v = 0; v < nvert; v++){
//elige aristas con flujo residual mayor a cero en el recorrido
residual = grafo[u][v].capacidad - grafo[u][v].flujo;
if(!visitado[v] && ( residual > 0)){
cola.push_back(v);//mete nodo a la cola
padre[v] = u;//guarda a su padre
visitado[u] = 1;//lo marca como visitado
}
}
}

//devolver estado del camino al sumidero al terminar el recorrido
return visitado[sumidero];
}
//algoritmo de ford-fulkerson
int ford_fulkerson(int fuente, int sumidero){
int i, j , u;
int flujomax, incremento, residual;

//los flujos a cero antes de iniciar el algoritmo
for(i = 0; i < nvert; i++)
for(j = 0; j < nvert; j++)
grafo[i][j].flujo = 0;
flujomax = 0;

//mientras existan caminos de flujo residual
while(BFS(fuente, sumidero)){
//busca el flujo minimo en el camino de f a s
incremento = INFINITO;//inicializa incremento a infinito

//busca el flujo residual mnimo en el camino de fuente a sumidero
for(u = sumidero; padre[u] != NULO; u = padre[u]){
residual = grafo[padre[u]][u].capacidad- grafo[padre[u]][u].flujo;
incremento = min( incremento, residual);
}

//actualiza los valores de flujo, flujo mximo y residual en el camino
for(u = sumidero; padre[u] != NULO; u = padre[u]){
//actualiza los valores en el sentido de fuente a sumidero
grafo[padre[u]][u].flujo += incremento;
//hace lo contrario en el sentido de sumidero a fuente
grafo[u][padre[u]].flujo -= incremento;
}
// muestra la ruta
for (u=sumidero; padre[u]!=(-1); u=padre[u]) {
printf("%d<-",u);
}
printf("%d aade %d de flujo adicional\n", fuente,incremento);
flujomax += incremento;
}//al salir del ciclo ya no quedan rutas de incremento de flujo

//se devuelve el ciclo maximo
return flujomax;
}
int main(){
int narist;
int a, b, c;
int fuente, sumidero;
int flujo;
int i, j;
//leer parametros del grafo
scanf("%d %d\n", &nvert, &narist);

//inicializar el grafo
inicia_grafo();

//leer las aristas
while(narist){
//leer arista (a,b) con capacidad c
scanf("%d %d %d\n", &a, &b, &c);
inserta_arista(a, b, c);
narist--;
}

//leer la consulta
scanf("%d %d\n", &fuente, &sumidero);
flujo = ford_fulkerson(fuente, sumidero);
printf("El flujo maximo entre %d y %d es %d\n", fuente, sumidero, flujo);

printf("El flujo entre los vertices quedo asi\n");

for(i = 0; i < nvert; i++)
for(j = 0; j < nvert; j++)
if( (i != j) && (grafo[i][j].flujo != 0) )
printf("( %d, %d) = %d\n", i, j, grafo[i][j].flujo);

return 0;
}
&istado 39 Jmplementacin de Ford>Fl?erson
&a implementacin (e se mestra en el listado anterior pede
mejorarse si combinan las propiedades de la matri2 con las listas+
(i2 teniendo el grafo almacenado de las dos formas. A($
debemos considerar (e las listas de ad'acencia solo sir%en para
almacenar los nodos ad'acentes ' se sige sando la matri2 para
consltar los %alores de los fljos ' de las capacidades de cada
arista. Debido a (e peden e"istir ms de na arista entre cado par
de %.rtices+ reslta #til tener na estrctra (e almacene datos sin
permitir repetidos como el mapa. De este modo+ el grafo se
almacenar$a como sige:
typedef pair<int, int> FLUJOS
vector< map<int, FLUJOS> > grafo;
Usando dic)as estrctras definidas en la S4& podemos sacar
el m"imo partido de la eficiencia de las listas de ad'acencia con la
facilidad de los mapas para locali2ar datos al tiempo (e se pede
iterar de manera eficiente sobre ellos. Se deja como ejercicio
modificar la implementacin para a6adir las mejoras sgeridas.
A continacin se mestra la solcin del problema C37
OJnternet @andSidt)P+ en donde se sa el algoritmo de Ford>
Fl?erson. !n este problema es importante se6alar (e se trata de n
grafo no dirigido ' (e el anc)o de banda se llena smando el %alor
absolto del fljo de datos 0es decir+ a($ no se cmple cap0+%1 G
>cap0%+11. !sto pro%oca (e la matri2 sea sim.trica ' (e el fljo
residal sea igal en ambos sentidos. !sto spone na posible
mejora si se toma en centa la simetr$a de la matri2 de cal(ier
forma ' para )acer ms simple de entender+ se )a dejado la solcin
basada en la matri2 0el tiempo de ejeccin es bastante aceptable
7.1171.
#include <iostream>
#include <list>
using namespace std;
#define MAXVERT 101
#define NULO -1
int G[MAXVERT][MAXVERT];
int nvert, narist, P[MAXVERT];
int camino(int f, int s){
int u, v, visitado[MAXVERT];
list<int> cola;

//inicializar la busqueda
for(u = 1; u <= nvert; u++){
visitado[u] = 0;
P[u] = NULO;
}

//meter fuente a la cola
cola.push_back(f);
visitado[f] = 1;
while(!cola.empty()){
//sacar nodo de la cola
u = cola.front(); cola.pop_front();
//recorrer los adyacentes a u
for(v = 1; v <= nvert; v++){
//si no fue visitado y tiene flujo residual
if(!visitado[v] && (G[u][v] > 0)){
cola.push_back(v);//meter adyacente a la cola
P[v] = u;//guardar al padre de v
visitado[v] = 1;//marcarlo como visitado
}
}
}

//si existe camino de f a s, entonces s fue visitado
return visitado[s];
}
int main(){
int u, v, c;
int f, s;
int flujo, menor;
int n;

n = 1;
while(1){

//leer numero de vertices en el grafo
cin >> nvert;
if(!nvert) break;

//leer fuente, sumidero, numero de aristas
cin >> f >> s >> narist;

//borrar el grafo
for(u = 1; u <= nvert; u++)
for(v = 1; v <= nvert; v++)
G[u][v] = 0;

//leer las aristas
while(narist){
//leer arista (u,v) con capacidad c
cin >> u >> v >> c;
//inserta aristas (u,v) y (v,u)
G[u][v] += c;//capacidad igual en ambos sentidos
G[v][u] += c;
narist--;
}

flujo = 0;
//Ford Fulkerson de f a s
while(camino(f, s)){

//encuentra la arista de menor peso en el camino
menor = G[P[s]][s];
for(v = s; P[v] != NULO; v = P[v])
if(menor > G[P[v]][v]) menor = G[P[v]][v];

//actualizar el flujo residual en el camino
for(v = s; P[v] != NULO; v = P[v]){
//actualiza (u,v) y (v,u)
G[P[v]][v] -= menor;//flujo residual igual en ambos sentidos
G[v][P[v]] -= menor;
}

//actualiza el valor del flujo neto de f a s
flujo += menor;
}

//imprime los resultados
cout << "Network " << n++ << endl;
cout << "The bandwidth is " << flujo << "." << endl;
cout << endl;
}

return 0;
}
&istado 3: Solcin del problema C37
Otro problema interesante (e se pede redcir a n problema
de fljo m"imo es el del aparejamiento bipartito m"imo. <ara este
problema se tiene n grafo bipartido+ donde bipartido significa que
se pueden identificar en el grafo dos subconuntos de v!rtices "
y # de tal manera que uno de los e$tremos de las aristas est% en
# y el otro "& "as aristas (u,v) donde u y v pertenecen al mismo
conunto no est%n permitidas&
!ste tipo de grafos sir%e para resol%er problemas como el de
asignacin de tareas. Si sponemos (e el conjnto R representa a
n grpo de trabajadores ' (e & corresponde a n conjnto de
tareas+ entonces las aristas representan la relacin de (e n
trabajador pede reali2ar determinada tarea. !l problema consiste en
asignar la ma'or cantidad de tareas para (e sean reali2adas por los
trabajadores.
!l planteamiento general del problema es el sigiente+ dado n
grafo no dirigido G G 0/+ !1+ n aparejamiento es n sbconjnto 5
en ! tal (e para todos los %.rtices % en /+ cando ms na arista de
5 es incidente en %. Se dice entonces (e el %.rtice % esta aparejado
en 5 si algna arista en 5 es incidente en %+ de otra forma % no esta
aparejado. Un aparejamiento 5 es m"imo si cmple con el
re(isito de ser de cardinalidad m"ima+ es decir (e para cal(ier
otro sbconjnto 5\+ se cmple ^5^ VG ^5\^.
!n este caso+ se restringir el ni%erso del problema a los
grafos bipartidos+ de tal forma (e / G & U R. De esta forma
podemos decir (e n %.rtice pertenece & pero no a R ' %ice%ersa+
dic)o de otro modo los conjnto & ' R son disjntos. !n este caso+
todas las aristas en ! tienen n %.rtice en & ' el otro en R.
Se pede sar el algoritmo de Ford>Fl?erson para resol%er
dic)o problema. Si tenemos el grafo bipartido G G 0/+ !1 deberemos
formar a partir de .l el grafo de fljos G\ G 0/\+ !\1 de la sigiente
manera. Dejemos (e /\ sea / U Zs+t[ donde s es la fente ' t el
smidero. Si la particin de G es & U R+ el conjnto de aristas en G\
son las aristas de ! dirigidas de & a R+ ms / ne%as aristas.
!"presado en la notacin de conjntos tenemos lo sigiente:
!\ G Z0s+1 tal (e esta en &[ U
Z0+%1 tal (e esta en &+ % en R ' 0+%1 en ![ U
Z0%+t1 tal (e % esta en R[.
<ara completar la constrccin del grafo de fljos es necesario
asignar na capacidad nitaria a cada arista en !\.

También podría gustarte