Floyd-Warshall
¿Qué es?
Section titled “¿Qué es?”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.
La Idea Central: El Intermediario
Section titled “La Idea Central: El Intermediario”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]);Definición Formal (DP)
Section titled “Definición Formal (DP)”Aunque la implementación suele hacerse “in-place” (sobre la misma matriz), el estado real de la DP es tridimensional:
Significa: “La distancia más corta desde el nodo hasta el nodo , usando solamente vértices intermedios del conjunto ”.
La transición es:
Inicialización de la Matriz
Section titled “Inicialización de la Matriz”Para que funcione, la matriz dist[N][N] debe prepararse así antes de los bucles:
- Diagonal:
dist[i][i] = 0(Distancia a uno mismo es 0). - Aristas:
dist[u][v] = w(Si existe arista directa). - Sin conexión:
dist[i][j] = INF(Un número muy grande, ej.1e18).
Implementación en C++
Section titled “Implementación en C++”#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"; }}Análisis y Complejidad
Section titled “Análisis y Complejidad”- Complejidad Temporal:
- El algoritmo consiste en tres bucles anidados que recorren todos los nodos ().
- Complejidad Espacial:
- Necesitamos almacenar la matriz de distancias completa.
¿Cuándo usarlo?
Section titled “¿Cuándo usarlo?”- Restricciones Pequeñas: Ideal cuando el número de nodos es pequeño ().
- Cálculo: operaciones (aprox. 1 segundo en C++).
- Gráficas Densas: Cuando tienes muchas aristas (), Floyd-Warshall es muy competitivo frente a correr Dijkstra desde cada nodo.
- Implementación Rápida: Es mucho más rápido de escribir y menos propenso a errores que Dijkstra o Bellman-Ford si lo permite.
Detección de Ciclos Negativos
Section titled “Detección de Ciclos Negativos”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 , significa que existe un ciclo negativo que involucra al nodo .
Ejercicios Recomendados
Section titled “Ejercicios Recomendados”Practica con estos problemas clásicos:
- 1334. Find the City… (LeetCode)
- Aplicación directa donde necesitas la matriz de distancias completa para filtrar vecinos.
- Shortest Routes II (CSES)
- El problema estándar: y muchas consultas () sobre distancias entre pares.