Skip to content
Clases

Floyd-Warshall

Es un algoritmo de programación dinámica que encuentra el camino más corto entre todos los pares de nodos en una gráfica (All-Pairs Shortest Path).

  • Versatilidad: Funciona en gráficas dirigidas y no dirigidas.
  • Pesos Negativos: ¡Sí! Maneja aristas negativas (a diferencia de Dijkstra).
  • Limitación: La gráfica no puede tener ciclos negativos.

El algoritmo intenta mejorar el camino entre dos nodos i y j probando pasar por un tercer nodo k. La pregunta clave es: “¿Es más rápido ir de i a j directo, o pasando por k?”

Esto se traduce en la Ecuación de Bellman para este problema:

dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);

Aunque la implementación suele hacerse “in-place” (sobre la misma matriz), el estado real de la DP es tridimensional:

dp[k][i][j]dp[k][i][j]

Significa: “La distancia más corta desde el nodo ii hasta el nodo jj, usando solamente vértices intermedios del conjunto {0,1,,k}\{0, 1, \dots, k\}”.

La transición es: dp[k][i][j]=min(dp[k1][i][j],dp[k1][i][k]+dp[k1][k][j])dp[k][i][j] = \min(dp[k-1][i][j], \quad dp[k-1][i][k] + dp[k-1][k][j])

Para que funcione, la matriz dist[N][N] debe prepararse así antes de los bucles:

  1. Diagonal: dist[i][i] = 0 (Distancia a uno mismo es 0).
  2. Aristas: dist[u][v] = w (Si existe arista directa).
  3. Sin conexión: dist[i][j] = INF (Un número muy grande, ej. 1e18).

#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e18;
int main(){
int n, m, q;
cin >> n >> m >> q;
// 1. Inicializar todo con INF
vector<vector<long long>> dist(n, vector<long long>(n, INF));
// 2. La distancia a uno mismo es 0
for (int i = 0; i < n; i++) dist[i][i] = 0;
// 3. Leer aristas
for(int i = 0; i < m; i++){
int u, v; long long w;
cin >> u >> v >> w;
u--; v--; // Convertir a 0-indexado si la entrada es 1-based
// Usamos min() por si hay múltiples aristas entre los mismos nodos
dist[u][v] = min(dist[u][v], w);
// Si la gráfica es NO dirigida, descomenta la siguiente línea:
// dist[v][u] = min(dist[v][u], w);
}
// 4. El Corazón de Floyd-Warshall (Orden: k -> i -> j)
for(int k = 0; k < n; k++){ // Nodo Intermedio (Pivote)
for(int i = 0; i < n; i++){ // Origen
for(int j = 0; j < n; j++){ // Destino
// Chequeo de seguridad para evitar overflow si INF es muy grande
if(dist[i][k] < INF && dist[k][j] < INF) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
// 5. Responder consultas en O(1)
while(q--){
int u, v; cin >> u >> v;
u--; v--;
if(dist[u][v] == INF) cout << "-1\n";
else cout << dist[u][v] << "\n";
}
}
  • Complejidad Temporal: O(V3)O(V^3)
    • El algoritmo consiste en tres bucles anidados que recorren todos los nodos (N×N×NN \times N \times N).
  • Complejidad Espacial: O(V2)O(V^2)
    • Necesitamos almacenar la matriz de distancias completa.
  1. Restricciones Pequeñas: Ideal cuando el número de nodos es pequeño (N500N \le 500).
    • Cálculo: 5003=125,000,000500^3 = 125,000,000 operaciones (aprox. 1 segundo en C++).
  2. Gráficas Densas: Cuando tienes muchas aristas (EV2E \approx V^2), Floyd-Warshall es muy competitivo frente a correr Dijkstra desde cada nodo.
  3. Implementación Rápida: Es mucho más rápido de escribir y menos propenso a errores que Dijkstra o Bellman-Ford si NN lo permite.

Floyd-Warshall tiene un “superpoder” oculto. Si al terminar el algoritmo revisas la diagonal principal y encuentras que dist[i][i] < 0 para algún nodo ii, significa que existe un ciclo negativo que involucra al nodo ii.


Practica con estos problemas clásicos: