Algoritmo de D´Esopo-Pape
Dado un grafo con vértices y aristas con pesos y un vértice de inicio . La tarea es encontrar el camino más corto del vértice a cada otro vértice.
El algoritmo de D´Esopo-Pape suele trabajar más rápido que el algoritmo de Dijkstra y el algoritmo de Bellman-Ford en la mayoría de los casos, y también funciona con aristas negativas. Sin embargo no para ciclos negativos.
Descripción
Sea el arreglo el que contiene las longitudes de los caminos más cortos, es decir, es la longitud actual del camino más corto del vértice al vértice . Inicialmente este arreglo se llena con infinito para cada vértice, excepto . Cuando el algoritmo termina, este arreglo contendrá las distancias más cortas.
Sea el arreglo el que contiene los ancestros actuales, es decir, es el ancestro directo del vértice en el camino más corto actual de a . Al igual que el arreglo , el arreglo cambia gradualmente durante el algoritmo y al final toma sus valores finales.
Ahora al algoritmo. En cada paso se mantienen tres conjuntos de vértices:
- - vértices para los cuales la distancia ya se calculó (aunque podría no ser la distancia final)
- - vértices para los cuales la distancia se está calculando actualmente
- - vértices para los cuales la distancia todavía no se calculó
Los vértices del conjunto se guardan en una cola bidireccional (deque).
En cada paso del algoritmo tomamos un vértice del conjunto (del frente de la cola). Sea el vértice seleccionado. Ponemos este vértice en el conjunto . Después iteramos sobre todas las aristas que salen de este vértice. Sea el otro extremo de la arista actual, y su peso.
- Si pertenece a , entonces se inserta en el conjunto insertándolo al final de la cola. se pone en .
- Si pertenece a , entonces intentamos mejorar el valor de : . Como ya está en , no hace falta insertarlo en ni en la cola.
- Si pertenece a , y si se puede mejorar , entonces mejoramos e insertamos el vértice de vuelta al conjunto , colocándolo al principio de la cola.
Y por supuesto, con cada actualización en el arreglo también hay que actualizar el elemento correspondiente en el arreglo .
Implementación
Usaremos un arreglo para guardar en qué conjunto está actualmente cada vértice.
struct Edge {
int to, w;
};
int n;
vector<vector<Edge>> adj;
const int INF = 1e9;
void shortest_paths(int v0, vector<int>& d, vector<int>& p) {
d.assign(n, INF);
d[v0] = 0;
vector<int> m(n, 2);
deque<int> q;
q.push_back(v0);
p.assign(n, -1);
while (!q.empty()) {
int u = q.front();
q.pop_front();
m[u] = 0;
for (Edge e : adj[u]) {
if (d[e.to] > d[u] + e.w) {
d[e.to] = d[u] + e.w;
p[e.to] = u;
if (m[e.to] == 2) {
m[e.to] = 1;
q.push_back(e.to);
} else if (m[e.to] == 0) {
m[e.to] = 1;
q.push_front(e.to);
}
}
}
}
}Complejidad
El algoritmo suele ser bastante rápido: en la mayoría de los casos, incluso más rápido que el algoritmo de Dijkstra. Sin embargo existen casos para los cuales el algoritmo toma tiempo exponencial, lo que lo hace inadecuado en el peor caso. Ver discusiones en Stack Overflow y Codeforces como referencia.