1. Árbol de expansión mínima
En teoría de grafos, un árbol de expansión, árbol generador o árbol recubridor T de un grafo conexo, no
dirigido G es un árbol compuesto por todos los vértices y algunas (quizá todas) de las aristas de G.
Informalmente, un árbol de expansión de G es una selección de aristas de G que forman un árbol que
cubre todos los vértices. Esto es, cada vértice está en el árbol, pero no hay ciclos. Por otro lado, todos
los puentes de G deben estar contenidos en T.
Un árbol de expansión o árbol recubridor de un grafo conexo G puede ser también definido como el
mayor conjunto de aristas de G que no contiene ciclos, o como el mínimo conjunto de aristas que
conecta todos los vértices.
Construcción
Un árbol de expansión de un grafo se puede encontrar, ya sea por búsqueda en profundidad o la
búsqueda en amplitud. Ambos de estos algoritmos exploran el gráfico dado, partiendo de un vértice
arbitrario, haciendo un bucle a través de los vecinos de los vértices que descubren y añadiendo cada
vecino sin explorar a una estructura de datos para ser explorado más adelante. Si el grafo examinado es
ponderado podemos utilizar el algoritmo de Kruskal.
Algoritmo de Kruskal
El algoritmo de Kruskal es un algoritmo de la teoría de grafos para encontrar un árbol recubridor
mínimo en un grafo conexo y ponderado. Es decir, busca un subconjunto de aristas que, formando un
árbol, incluyen todos los vértices y donde el valor total de todas las aristas del árbol es el mínimo. Si el
grafo no es conexo, entonces busca un bosque expandido mínimo
Funciona de la siguiente manera:
1. se crea un bosque B (un conjunto de árboles), donde cada vértice del grafo es un árbol separado
2. se crea un conjunto C que contenga a todas las aristas del grafo
3. mientras C es no vacío
1. eliminar una arista de peso mínimo de C
2. si esa arista conecta dos árboles diferentes se añade al bosque, combinando los dos árboles
en un solo árbol
3. en caso contrario, se desecha la arista
2. Pseudocódigo Kruskal(G)
Para cada v en V[G] hacer
Nuevo conjunto C(v) ← {v}.
Nuevo heap Q que contiene todas las aristas de G, ordenando por su peso.
Defino un árbol T ← Ø
// n es el número total de vértices
Mientras T tenga menos de n-1 vertices hacer
(u,v) ← Q.sacarMin()
// previene ciclos en T. agrega (u,v) si u y v están diferentes
componentes en el conjunto.
// Nótese que C(u) devuelve la componente a la que pertenece u.
if C(v) ≠ C(u) then
Agregar arista (v,u) a T.
Merge C(v) y C(u) en el conjunto
Return arbol T
Ejemplo
Este es el grafo original. Los números de las aristas indican su peso.
Ninguna de las aristas está resaltada.
AD y CE son las aristas más cortas, con peso 5, y AD se ha elegido
arbitrariamente, por tanto se resalta.
Sin embargo, ahora es CE la arista más pequeña que no forma
ciclos, con peso 5, por lo que se resalta como segunda arista.
3. La siguiente arista, DF con peso 6, ha sido resaltada utilizando el
mismo método.
Las siguientes aristas más pequeñas son AB y BE, ambas con peso
7. AB se elige arbitrariamente, y se resalta. La arista BD se resalta
en rojo, porque formaría un ciclo ABD si se hubiera elegido.
El proceso continúa marcando las aristas, BE con peso 7. Muchas
otras aristas se marcan en rojo en este paso: BC (formaría el
cicloBCE), DE (formaría el ciclo DEBA), y FE (formaría el
ciclo FEBAD).
Finalmente, el proceso termina con la arista EG de peso 9, y se ha
encontrado el árbol expandido mínimo con peso total de 39.
4. El camino más corto
En la teoría de grafos, el problema del camino más corto es el problema que consiste en encontrar un
camino entre dos vértices (o nodos) de tal manera que la suma de los pesos de las aristas que lo
constituyen es mínima. Un ejemplo de esto es encontrar el camino más rápido para ir de una ciudad a
otra en un mapa. En este caso, los vértices representarían las ciudades y las aristas las carreteras que las
unen, cuya ponderación viene dada por el tiempo que se emplea en atravesarlas.
Algoritmos
Los algoritmos más importantes para resolver este problema son:
• Algoritmo de Dijkstra, resuelve el problema de los caminos más cortos desde un único vértice
origen hasta todos los otros vértices del grafo.
• Algoritmo de Bellman - Ford, resuelve el problema de los caminos más cortos desde un origen
si la ponderación de las aristas es negativa.
• Algoritmo de Búsqueda A*, resuelve el problema de los caminos más cortos entre un par de
vértices usando la heurística para intentar agilizar la búsqueda.
• Algoritmo de Floyd - Warshall, resuelve el problema de los caminos más cortos entre todos los
vértices.
• Algoritmo de Johnson, resuelve el problema de los caminos más cortos entre todos los vértices
y puede ser más rápido que el de Floyd-Warshall en grafos de baja densidad.
• Algoritmo de Viterbi, resuelve el problema del camino estocástico más corto con un peso
probabilístico adicional en cada vértice.
Algoritmo de Dijkstra
El algoritmo de Dijkstra, también llamado algoritmo de caminos mínimos, es un algoritmo para la
determinación del camino más corto dado un vértice origen al resto de los vértices en un grafo con
pesos en cada arista. Su nombre se refiere a Edsger Dijkstra, quien lo describió por primera vez en
1959.
La idea subyacente en este algoritmo consiste en ir explorando todos los caminos más cortos que parten
del vértice origen y que llevan a todos los demás vértices; cuando se obtiene el camino más corto desde
el vértice origen, al resto de vértices que componen el grafo, el algoritmo se detiene. El algoritmo es
una especialización de la búsqueda de costo uniforme, y como tal, no funciona en grafos con aristas de
5. coste negativo (al elegir siempre el nodo con distancia menor, pueden quedar excluidos de la búsqueda
nodos que en próximas iteraciones bajarían el costo general del camino al pasar por una arista con
costo negativo).
Algoritmo
1. Teniendo un grafo dirigido ponderado de N nodos no aislados, sea x el nodo inicial, un vector D
de tamaño N guardará al final del algoritmo las distancias desde x al resto de los nodos.
2. Inicializar todas las distancias en D con un valor infinito relativo ya que son desconocidas al
principio, exceptuando la de x que se debe colocar en 0 debido a que la distancia de x a x sería
0.
3. Sea a = x (tomamos a como nodo actual).
1. Recorremos todos los nodos adyacentes de a, excepto los nodos marcados, llamaremos a
estos nodos no marcados vi.
2. Para el nodo actual, calculamos la distancia tentativa desde dicho nodo a sus vecinos con la
siguiente fórmula: dt(vi) = Da + d(a,vi). Es decir, la distancia tentativa del nodo ‘vi’ es la
distancia que actualmente tiene el nodo en el vector D más la distancia desde dicho el nodo
‘a’ (el actual) al nodo vi. Si la distancia tentativa es menor que la distancia almacenada en el
vector, actualizamos el vector con esta distancia tentativa. Es decir: Si dt(vi) < Dvi → Dvi =
dt(vi)
4. Marcamos como completo el nodo a.
5. Tomamos como próximo nodo actual el de menor valor en D (puede hacerse almacenando los
valores en una cola de prioridad) y volvemos al paso 3 mientras existan nodos no marcados.
6. Una vez terminado al algoritmo, D estará completamente lleno.
6. Pseudocódigo DIJKSTRA (Grafo G, nodo_fuente s)
para u ∈ V[G] hacer
distancia[u] = INFINITO
padre[u] = NULL
visto[u] = false
distancia[s] = 0
adicionar (cola, (s, distancia[s]))
mientras que cola no es vacía hacer
u = extraer_mínimo(cola)
visto[u] = true
para todos v ∈ adyacencia[u] hacer
si no visto[v] y distancia[v] > distancia[u] + peso (u, v) hacer
distancia[v] = distancia[u] + peso (u, v)
padre[v] = u
adicionar(cola,(v, distancia[v]))
Ejemplo
Inicializa, selecciona el nodo de inicio predeterminado, etiquetar los nodos.
Reemplaza las etiquetas con la distancia de ir de a a cualquier nodo adyacente
1
2
3
4
5
6
2
4
21
3
4
2
3
2
1
0
∞
∞
∞
∞
∞
2
3
4
5
6
2
4
21
3
4
2
3
2
2
4
0
1
∞
∞
∞
∞
∞
7. Selecciona el predecesor mediante el que genere el costo más pequeño y continua
Cuando la suma no mejora entonces se omite el reetiquetado
1
2
3
4
5
6
2
4
21
3
4
2
3
2
2
4
6
43
0
∞
∞
∞
1
2 4
5
6
2
4
21
3
4
2
3
2
2
3
6
4
0
3
∞
1
2 4
5
6
2
4
21
3
4
2
3
2
0
3
2
3
6
4
∞
9. Flujo máximo
En teoría de grafos, una red de flujo es un grafo dirigido donde existen dos vértices especiales, uno
llamado fuente, al que se le asocia un flujo positivo y otro llamado sumidero que tiene un flujo
negativo y a cada arista se le asocia cierta capacidad positiva. En cada vértice diferente a los dos
especiales se mantiene la ley de corrientes de Kirchoff, en donde la suma de flujos entrantes a un
vértice debe ser igual a la suma de flujos que salen de él. Puede ser utilizada para modelar el tráfico en
un sistema de autopistas, fluidos viajando en tuberías, corrientes eléctricas en circuitos eléctricos o
sistemas similares por lo que viaje algo entre nodos.
Algoritmo de flujo máximo
Tenemos el conocido problema de flujo máximo o maximal: ¿cuál es la tasa mayor a la cual el material
puede ser transportado de la fuente al sumidero sin violar ninguna restricción de capacidad?
En otras palabras, el problema consiste en determinar la máxima capacidad de flujo que puede ingresar
a través de la fuente y salir por el nodo de destino.
Algoritmo de Ford-Fulkerson
• El procedimiento para obtener el flujo máximo de una red, consiste en seleccionar repetidas
veces cualquier trayectoria de la fuente al destino y asignar el flujo máximo posible en esa
trayectoria.
• Capacidad residual: es la capacidad adicional de flujo que un arco puede llevar:
• cf(u,v)= c(u,v) - f(u,v),
• Dada una red de flujo máximo, plantee la red residual asociada.
• Encuentre la trayectoria de la fuente al destino con capacidad de flujo estrictamente positivo (si
no existe alguno, es por que se ha encontrado el óptimo).
• Examine estas trayectorias para encontrar la rama o arco con la menor capacidad de flujo
restante e incremente en éste valor, la capacidad del flujo en sentido contrario.
• Determine todas las trayectorias estrictamente positivas, hasta que no se permita flujo del nodo
a un nodo destino.
10. Pseudocódigo Ford-Fulkerson(G,s,t) {
for (cada arco (u,v) de E) {
f[u,v]= 0;
f[v,u]= 0;
}
while (exista un camino p desde s a t en la red residual Gf) {
cf(p) = min{cf(u,v): (u,v) está sobre p};
for (cada arco (u,v) en p) {
f[u,v]= f[u,v] + cf(p);
f[v,u]= - f[u,v];
}
}
}
Ejemplo
red de inicio
Encuentra un camino p de s a t en G
11. Determina la capacidad de p y actualiza los flujos
Encuentra otro camino p de s a t
Determina la capacidad y actualiza
12. Y así iterativamente hasta no encontrar caminos en G
Finalmente determina la red óptima
13. El método de la Ruta Crítica conocida también por CPM por sus siglas en inglés (Critical Path Method)
es una metodología de la gestión de proyectos que nos permite entre otros aspectos estimar la duración
de un Proyecto. Para este propósito es necesario conocer las actividades que contempla el proyecto, su
duración en una unidad de tiempo y el orden en el cuál deben ser realizadas (por ejemplo algunas
actividades se pueden desarrollar sólo cuando una o varias actividades previas han sido completadas).
Cómo obtener la Ruta Crítica de un Proyecto
El ejemplo a continuación muestra en detalle la aplicación del método de ruta crítica a un proyecto que
consta de 9 actividades cuyos tiempos estimados se encuentran en semanas. Adicionalmente en la
columna “Predecesor” se establece el orden en el cual se deben realizar las distintas actividades, por
ejemplo la actividad G se puede realizar una vez completada la actividad D y F.
En este contexto resulta de utilidad desarrollar un diagrama o representación gráfica del
proyecto donde cada nodo representa una actividad y las flechas un camino o ruta.
Se puede observar que las actividades iniciales son A y B y la actividad final es I. Por tanto
laduración del proyecto será aquella ruta o camino más largo que comenzando en A (o en
B) termine en I. Luego, dado el tamaño reducido de este ejemplo es posible enumerar todas
14. las posibilidades que satisfacen la condición anterior:
•Ruta: A-C-I: 5[sem]+4[sem]+2[sem]=11[sem]
•Ruta: A-D-G-I: 5[sem]+3[sem]+14[sem]+2[sem]=24[sem]
•Ruta: A-E-F-G-I: 5[sem]+1[sem]+4[sem]+14[sem]+2[sem]=26[sem]
•Ruta: B-H-I: 6[sem]+12[sem]+2[sem]=20[sem]
La ruta crítica por tanto es A-E-F-G-I lo que determina que la duración del proyecto es
de26[sem]. Adicionalmente podemos estimar cuándo es lo más pronto que se puede
comenzar cada actividad (inicio más cercano o IC – color rojo) y cuándo es lo más pronto
que se puede terminar una actividad (término más cercano o TC – color azul).
En forma complementaria se puede obtener el tiempo más lejano en el cual se puede
terminar una actividad sin atrasar el proyecto (término más lejano o TL – verde) y cuándo
es lo más tarde que se puede comenzar una actividad sin retrasar el proyecto (inicio más
lejano o IL – naranjo). Para obtener dichos tiempos retrocedemos desde la actividad final (I)
hacia las actividades iniciales (A y B).
15. En este contexto se define el término Holgura (H) o Slack como el tiempo máximo que una
actividad se puede retrasar en su inicio sin que esto afecte el tiempo estimado para terminar
el proyecto como un todo:
Holgura = IL – IC = TL – TC
El siguiente diagrama muestra la ruta del proyecto con el cálculo de las holguras de cada una
de las actividades. Se puede apreciar por ejemplo que la actividad B se puede retrasar un
máximo de 6[sem] (su holgura) y aun así estar en condiciones de terminar el proyecto en
26[sem]. Adicionalmente las actividades que pertenecen a la ruta crítica tienen
holgura igual a cero, lo que en este ejemplo en particular permite identificar una ruta
única: A-E-F-G-I(notar que en general un proyecto puede tener más de una ruta o camino
crítico).