Matemáticas II · Tema 7 · Sección 7.4

Ejercicios resueltos de caminos mínimos

4 ejercicios resueltos paso a paso del tema 7 de Matemáticas II (Teoría de grafos). El enunciado está a la vista y la solución, plegada: intenta cada ejercicio antes de abrirla.

Ejercicio 1

Dificultad: Básico

Aplicar el algoritmo de Dijkstra al grafo dirigido con vértices {S,A,B,C,D,T}\{S, A, B, C, D, T\} y aristas con pesos: (S,A,4)(S,A,4), (S,B,2)(S,B,2), (A,C,3)(A,C,3), (A,D,6)(A,D,6), (B,A,1)(B,A,1), (B,D,5)(B,D,5), (C,T,2)(C,T,2), (D,C,1)(D,C,1), (D,T,4)(D,T,4) para encontrar los caminos mínimos desde SS a todos los demás vértices.

Ver solución paso a paso4 pasos
  1. Paso 1
    Inicialización del algoritmo
    • Distancias: d[S]=0d[S] = 0, d[v]=∞d[v] = \infty para v≠Sv \neq S
    • Conjunto visitados: Vvisited=∅V_{visited} = \emptyset
    • Predecesores: pred[v]=nullpred[v] = null para todo vv
  2. Paso 2
    Ejecución iterativa de Dijkstra
    It.Seleccionard[S]d[S]d[A]d[A]d[B]d[B]d[C]d[C]d[D]d[D]d[T]d[T]Visitados
    0-0∞\infty∞\infty∞\infty∞\infty∞\infty{}
    1S042∞\infty∞\infty∞\infty{S}
    2B032∞\infty7∞\infty{S,B}
    3A03267∞\infty{S,B,A}
    4C032678{S,B,A,C}
    5D032678{S,B,A,C,D}
    6T032678{S,B,A,C,D,T}
  3. Paso 3
    Detalles de cada iteración

    Iteración 1 (seleccionar S):

    • Relajar (S,A)(S,A): d[A]=min⁡(∞,0+4)=4d[A] = \min(\infty, 0 + 4) = 4, pred[A]=Spred[A] = S
    • Relajar (S,B)(S,B): d[B]=min⁡(∞,0+2)=2d[B] = \min(\infty, 0 + 2) = 2, pred[B]=Spred[B] = S

    Iteración 2 (seleccionar B):

    • Relajar (B,A)(B,A): d[A]=min⁡(4,2+1)=3d[A] = \min(4, 2 + 1) = 3, pred[A]=Bpred[A] = B
    • Relajar (B,D)(B,D): d[D]=min⁡(∞,2+5)=7d[D] = \min(\infty, 2 + 5) = 7, pred[D]=Bpred[D] = B

    Iteración 3 (seleccionar A):

    • Relajar (A,C)(A,C): d[C]=min⁡(∞,3+3)=6d[C] = \min(\infty, 3 + 3) = 6, pred[C]=Apred[C] = A
    • Relajar (A,D)(A,D): d[D]=min⁡(7,3+6)=7d[D] = \min(7, 3 + 6) = 7 (no mejora)

    Iteración 4 (seleccionar C):

    • Relajar (C,T)(C,T): d[T]=min⁡(∞,6+2)=8d[T] = \min(\infty, 6 + 2) = 8, pred[T]=Cpred[T] = C

    Iteración 5 (seleccionar D):

    • Relajar (D,C)(D,C): d[C]=min⁡(6,7+1)=6d[C] = \min(6, 7 + 1) = 6 (no mejora)
    • Relajar (D,T)(D,T): d[T]=min⁡(8,7+4)=8d[T] = \min(8, 7 + 4) = 8 (no mejora)
  4. Paso 4
    Reconstruir caminos mínimos

    Desde S a cada vértice:

    • S → A: S → B → A, distancia = 3
    • S → B: S → B, distancia = 2
    • S → C: S → B → A → C, distancia = 6
    • S → D: S → B → D, distancia = 7
    • S → T: S → B → A → C → T, distancia = 8

Ejercicio 2

Dificultad: Intermedio

Comparar los resultados del algoritmo de Dijkstra y del algoritmo de Floyd-Warshall aplicados al mismo grafo. Analizar cuándo es preferible usar cada uno.

Ver solución paso a paso6 pasos
  1. Paso 1
    Grafo de prueba

    Usar el grafo dirigido con vértices {1, 2, 3, 4} y aristas:

    (1,2,3)(1{,}2{,}3), (1,4,7)(1{,}4{,}7), (2,3,1)(2{,}3{,}1), (2,4,2)(2{,}4{,}2), (3,4,6)(3{,}4{,}6), (4,1,2)(4{,}1{,}2), (4,3,4)(4{,}3{,}4)

  2. Paso 2
    Aplicar algoritmo de Floyd-Warshall

    Matriz de distancias inicial:

    D(0)=(03∞7∞012∞∞062∞40)D^{(0)} = \begin{pmatrix} 0 & 3 & \infty & 7 \\ \infty & 0 & 1 & 2 \\ \infty & \infty & 0 & 6 \\ 2 & \infty & 4 & 0 \end{pmatrix}

    Iteración k=1 (via vértice 1):

    D(1)=(03∞7∞012∞∞062540)D^{(1)} = \begin{pmatrix} 0 & 3 & \infty & 7 \\ \infty & 0 & 1 & 2 \\ \infty & \infty & 0 & 6 \\ 2 & 5 & 4 & 0 \end{pmatrix}

    Iteración k=2 (via vértice 2):

    D(2)=(0345∞012∞∞062540)D^{(2)} = \begin{pmatrix} 0 & 3 & 4 & 5 \\ \infty & 0 & 1 & 2 \\ \infty & \infty & 0 & 6 \\ 2 & 5 & 4 & 0 \end{pmatrix}

    Iteración k=3 (via vértice 3):

    D(3)=(0345∞012∞∞062540)D^{(3)} = \begin{pmatrix} 0 & 3 & 4 & 5 \\ \infty & 0 & 1 & 2 \\ \infty & \infty & 0 & 6 \\ 2 & 5 & 4 & 0 \end{pmatrix}

    Iteración k=4 (via vértice 4):

    D(4)=(03454012811062540)D^{(4)} = \begin{pmatrix} 0 & 3 & 4 & 5 \\ 4 & 0 & 1 & 2 \\ 8 & 11 & 0 & 6 \\ 2 & 5 & 4 & 0 \end{pmatrix}

  3. Paso 3
    Aplicar Dijkstra desde cada vértice

    Desde vértice 1: d(1)=0, d(2)=3, d(3)=4, d(4)=5

    Desde vértice 2: d(1)=4, d(2)=0, d(3)=1, d(4)=2

    Desde vértice 3: d(1)=8, d(2)=11, d(3)=0, d(4)=6

    Desde vértice 4: d(1)=2, d(2)=5, d(3)=4, d(4)=0

  4. Paso 4
    Verificar coincidencia de resultados

    Ambos algoritmos producen la misma matriz de distancias mínimas:

    Distancias=(03454012811062540)\text{Distancias} = \begin{pmatrix} 0 & 3 & 4 & 5 \\ 4 & 0 & 1 & 2 \\ 8 & 11 & 0 & 6 \\ 2 & 5 & 4 & 0 \end{pmatrix}

  5. Paso 5
    Análisis de complejidad

    Floyd-Warshall:

    • Complejidad temporal: O(n3)O(n^3)
    • Complejidad espacial: O(n2)O(n^2)
    • Calcula todos los pares de caminos mínimos

    Dijkstra (desde todos los vértices):

    • Complejidad temporal: O(n⋅(n+m)log⁡n)O(n \cdot (n + m) \log n) con heap binario
    • Para grafos densos (m=O(n2)m = O(n^2)): O(n3log⁡n)O(n^3 \log n)
    • Complejidad espacial: O(n)O(n) por ejecución
  6. Paso 6
    Criterios de selección

    Usar Floyd-Warshall cuando:

    • Necesitas distancias entre todos los pares de vértices
    • El grafo es denso (m≈n2m \approx n^2)
    • El grafo es pequeño (n≤400n \leq 400)
    • Pueden existir aristas con pesos negativos (pero sin ciclos negativos)

    Usar Dijkstra cuando:

    • Solo necesitas caminos desde una fuente específica
    • El grafo es disperso (m≪n2m \ll n^2)
    • El grafo es grande (n>1000n > 1000)
    • Todos los pesos son no negativos
    • Necesitas reconstruir los caminos completos, no solo las distancias

    Caso híbrido:

    Para k<nk < n fuentes específicas: ejecutar Dijkstra kk veces puede ser más eficiente que Floyd-Warshall si k≪nk \ll n.

Ejercicio 3

Dificultad: Avanzado

Modificar el algoritmo de Dijkstra para encontrar el segundo camino más corto desde un vértice fuente. Aplicar al grafo del ejercicio anterior.

Ver solución paso a paso7 pasos
  1. Paso 1
    Algoritmo para el segundo camino más corto

    Estrategia: Usar una variación del algoritmo de Dijkstra que mantiene los dos mejores caminos a cada vértice.

    Modificaciones:

    • Mantener dos distancias por vértice: d1[v]d_1[v] (mínima) y d2[v]d_2[v] (segunda mínima)
    • Usar un heap que puede contener hasta 2 entradas por vértice
    • Procesar cada vértice hasta 2 veces
  2. Paso 2
    Implementación del algoritmo modificado

    Grafo de ejemplo: Del ejercicio anterior, desde vértice 1.

    Inicialización:

    • d1[1]=0d_1[1] = 0, d2[1]=∞d_2[1] = \infty
    • d1[v]=d2[v]=∞d_1[v] = d_2[v] = \infty para v≠1v \neq 1
  3. Paso 3
    Ejecución detallada
    IteraciónExtraerTipoProcesar aristaActualización
    1(1, 0)1º(1,2,3)(1{,}2{,}3)d1[2]=3d_1[2] = 3
    (1,4,7)(1{,}4{,}7)d1[4]=7d_1[4] = 7
    2(2, 3)1º(2,3,1)(2{,}3{,}1)d1[3]=4d_1[3] = 4
    (2,4,2)(2{,}4{,}2)d1[4]=5d_1[4] = 5 (mejor), d2[4]=7d_2[4] = 7
    3(3, 4)1º(3,4,6)(3{,}4{,}6)No mejora d1[4]d_1[4], pero d2[4]=min⁡(7,4+6)=7d_2[4] = \min(7, 4+6) = 7
    4(4, 5)1º(4,1,2)(4{,}1{,}2)d2[1]=5+2=7d_2[1] = 5+2 = 7
    (4,3,4)(4{,}3{,}4)d2[3]=5+4=9d_2[3] = 5+4 = 9
    5(4, 7)2º(4,1,2)(4{,}1{,}2)d2[1]=min⁡(7,7+2)=7d_2[1] = \min(7, 7+2) = 7
    (4,3,4)(4{,}3{,}4)d2[3]=min⁡(9,7+4)=9d_2[3] = \min(9, 7+4) = 9
    6(1, 7)2º(1,2,3)(1{,}2{,}3)d2[2]=7+3=10d_2[2] = 7+3 = 10
    (1,4,7)(1{,}4{,}7)No actualiza (14 > 7)
  4. Paso 4
    Resultados del algoritmo

    Desde vértice 1:

    • Vértice 2: 1º camino = 3, 2º camino = 10
    • Vértice 3: 1º camino = 4, 2º camino = 9
    • Vértice 4: 1º camino = 5, 2º camino = 7
  5. Paso 5
    Reconstruir los segundos caminos

    Para reconstruir caminos, mantener predecesores para ambas distancias:

    Segundos caminos desde vértice 1:

    • 1 → 2: La única arista que llega a 2 es (1,2), así que no hay un segundo camino simple
    • Segundo recorrido (con ciclo): 1 → 2 → 4 → 1 → 2 = 3 + 2 + 2 + 3 = 10
    • 1 → 3: 1 → 2 → 4 → 3, peso = 3 + 2 + 4 = 9
    • 1 → 4: 1 → 2 → 4 → 1 → 4, pero esto contiene ciclo
    • Segundo camino válido: 1 → 4 (directo), peso = 7
  6. Paso 6
    Verificación manual

    Todos los caminos de 1 a 4:

    1. Directo: 1 → 4, peso = 7
    2. Via 2: 1 → 2 → 4, peso = 3 + 2 = 5 ✓\checkmark (mínimo)
    3. Via 2, luego ciclo: 1 → 2 → 4 → 1 → 4, peso = 5 + 2 + 7 = 14

    Resultado correcto: 1º camino = 5, 2º camino = 7

  7. Paso 7
    Complejidad del algoritmo
    • Temporal: O((n+m)log⁡n)O((n + m) \log n) (igual que Dijkstra estándar)
    • Espacial: O(n)O(n) para almacenar las dos distancias por vértice
    • Cada vértice se procesa a lo sumo 2 veces

Ejercicio 4

Dificultad: Experto

Diseñar un algoritmo para detectar ciclos negativos en un grafo dirigido usando el algoritmo de Bellman-Ford. Aplicar a un grafo que contenga un ciclo negativo.

Ver solución paso a paso7 pasos
  1. Paso 1
    Fundamento teórico del algoritmo de Bellman-Ford

    Principio: Si no hay ciclos negativos, las distancias mínimas se estabilizan en a lo sumo n−1n-1 iteraciones.

    Detección de ciclos negativos: Si después de n−1n-1 iteraciones, alguna distancia puede seguir mejorando, existe un ciclo negativo.

  2. Paso 2
    Algoritmo de Bellman-Ford modificado

    Bellman-Ford-Detect-Negative-Cycle(G, s):

    1. Inicializar d[s] = 0, d[v] = ∞\infty para v ≠ s
    2. Para i = 1 hasta n-1:

    Para cada arista (u,v) con peso w:

    Si d[u] + w < d[v]:

    d[v] = d[u] + w

    1. Para cada arista (u,v) con peso w:

    Si d[u] + w < d[v]:

    RETURN "Ciclo negativo detectado"

    1. RETURN "No hay ciclos negativos"
  3. Paso 3
    Grafo de prueba con ciclo negativo

    Vértices: {A, B, C, D}

    Aristas dirigidas con pesos:

    • (A,B,1)(A,B,1)
    • (B,C,−3)(B,C,-3)
    • (C,D,2)(C,D,2)
    • (D,B,−2)(D,B,-2) ← Esta arista crea el ciclo negativo
    • (A,D,5)(A,D,5)

    Ciclo negativo: B → C → D → B con peso total: −3+2+(−2)=−3-3 + 2 + (-2) = -3

  4. Paso 4
    Ejecución del algoritmo desde vértice A

    Inicialización: d[A]=0d[A] = 0, d[B]=d[C]=d[D]=∞d[B] = d[C] = d[D] = \infty

    Iteración 1:

    • Relajar (A,B)(A,B): d[B]=min⁡(∞,0+1)=1d[B] = \min(\infty, 0+1) = 1
    • Relajar (A,D)(A,D): d[D]=min⁡(∞,0+5)=5d[D] = \min(\infty, 0+5) = 5
    • Estado: d[A]=0,d[B]=1,d[C]=∞,d[D]=5d[A]=0, d[B]=1, d[C]=\infty, d[D]=5

    Iteración 2:

    • Relajar (B,C)(B,C): d[C]=min⁡(∞,1+(−3))=−2d[C] = \min(\infty, 1+(-3)) = -2
    • Relajar (C,D)(C,D): d[D]=min⁡(5,−2+2)=0d[D] = \min(5, -2+2) = 0
    • Estado: d[A]=0,d[B]=1,d[C]=−2,d[D]=0d[A]=0, d[B]=1, d[C]=-2, d[D]=0

    Iteración 3 (n-1 = 3 para 4 vértices):

    • Relajar (D,B)(D,B): d[B]=min⁡(1,0+(−2))=−2d[B] = \min(1, 0+(-2)) = -2
    • Relajar (B,C)(B,C): d[C]=min⁡(−2,−2+(−3))=−5d[C] = \min(-2, -2+(-3)) = -5
    • Relajar (C,D)(C,D): d[D]=min⁡(0,−5+2)=−3d[D] = \min(0, -5+2) = -3
    • Estado: d[A]=0,d[B]=−2,d[C]=−5,d[D]=−3d[A]=0, d[B]=-2, d[C]=-5, d[D]=-3
  5. Paso 5
    Verificación de ciclo negativo (iteración extra)

    Iteración 4 (verificación):

    • Relajar (D,B)(D,B): d[B]=min⁡(−2,−3+(−2))=−5d[B] = \min(-2, -3+(-2)) = -5 ✓\checkmark Mejora detectada
    • Relajar (B,C)(B,C): d[C]=min⁡(−5,−5+(−3))=−8d[C] = \min(-5, -5+(-3)) = -8 ✓\checkmark Mejora detectada

    Conclusión: Se detectó un ciclo negativo porque las distancias siguieron mejorando.

  6. Paso 6
    Identificar vértices afectados por el ciclo negativo

    Algoritmo extendido: Después de detectar el ciclo, marcar todos los vértices alcanzables desde los vértices que siguen mejorando.

    Marcar-Vertices-Ciclo-Negativo():

    1. Ejecutar n-1 iteraciones normales
    2. En la iteración de verificación:
      • Marcar vértices cuyas distancias mejoran
    3. Hacer BFS/DFS desde vértices marcados
      • Marcar todos los alcanzables

    Vértices afectados: B, C, D (todos están en el ciclo o son alcanzables desde él)

    Vértices no afectados: A (no está en camino hacia el ciclo negativo)

  7. Paso 7
    Aplicación práctica

    Detección de arbitraje en sistemas financieros:

    • Vértices = monedas
    • Aristas = tasas de cambio (logaritmos negativos)
    • Ciclo negativo = oportunidad de arbitraje

    Detección de inconsistencias en sistemas de restricciones:

    • Vértices = variables
    • Aristas = restricciones de diferencia
    • Ciclo negativo = sistema inconsistente