Para resolvermos esse problema vamos fazer uma modificação no Dijkstra. Partindo do um vértice com distância atual , vamos considerar os filhos , que tem uma distância já calculada, e são ligados por uma aresta de peso .
No algoritmo vamos considerar dois casos:
- Caso : Então vale a pena colocar essa aresta, e gravamos que o menor caminho chegando na aresta , parte de
- Caso : Então existe outro menor caminho chegando em , agora por . Podemos adicionar num vetor que o menor caminho chegando na aresta também parte de
Após processarmos esse Dijkstra fazemos uma DFS, partindo do vértice final, que passa pelos vértices que formam o menor caminho chegando no vértice atual (partindo do vértice de onde começamos o Dijkstra). Essa vetor de adjacência foi calculado do Dijkstra acima.
Agora para todo vértice temos as arestas proíbidas, ou seja, que fazem parte de algum menor caminho chegando no vértice de destino. Uma maneira fácil de remover essas arestas é dar sort em ambos os vetores de adjacência, e usar set_difference para gravar no vetor de adjcência original os caminhos que somente aparecem em um dos vetores.
Com o vetor de adjacência sem arestas que fazem parte do menor caminho já computado, podemos rodar o Dikjstra novamente e assim teremos a distância partindo do vértice inicial para o vétice final.
Código para melhor entendimento:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bits/stdc++.h> | |
#define ff first | |
#define ss second | |
using namespace std; | |
typedef pair<int, int> ii; | |
const int inf = 0x3f3f3f3f; | |
const int maxn = 510; | |
vector<ii> v[maxn]; | |
vector<ii> mir[maxn]; | |
vector<ii> inv[maxn]; | |
int d[maxn]; | |
int vis[maxn]; | |
void dijkstra(int ini) | |
{ | |
memset(d, 0x3f, maxn*4); | |
d[ini] = 0; | |
for (int i = 0; i < maxn; i++) mir[i].clear(); | |
priority_queue<ii, vector<ii>, greater<ii> > pq; | |
pq.push({0, ini}); | |
while (!pq.empty()) { | |
int w, at; | |
tie(w, at) = pq.top(); | |
pq.pop(); | |
if (w > d[at]) continue; | |
for (ii uu : v[at]) { | |
int u = uu.ff, ww = uu.ss; | |
if (d[u] == w + ww) { | |
mir[u].push_back({at, ww}); | |
} | |
else if (d[u] > w + ww) { | |
mir[u].clear(); | |
d[u] = w + ww; | |
mir[u].push_back({at, ww}); | |
pq.push({d[u], u}); | |
} | |
} | |
} | |
} | |
void dfs(int x) | |
{ | |
vis[x] = 1; | |
for (ii uu : mir[x]) { | |
int u = uu.ff, w = uu.ss; | |
inv[u].push_back({x, w}); | |
if (vis[u]) continue; | |
dfs(u); | |
} | |
} | |
int main() | |
{ | |
ios_base::sync_with_stdio(false); | |
cin.tie(0); | |
while (true) { | |
int n, m; | |
cin >> n >> m; | |
if (n == 0 and m == 0) break; | |
for (int i = 0; i < n; i++) { | |
mir[i].clear(); | |
inv[i].clear(); | |
v[i].clear(); | |
vis[i] = 0; | |
} | |
int ini, fim; | |
cin >> ini >> fim; | |
for (int i = 0; i < m; i++) { | |
int a, b, w; | |
cin >> a >> b >> w; | |
v[a].push_back({b, w}); | |
} | |
dijkstra(ini); | |
dfs(fim); | |
for (int i = 0; i < n; i++) { | |
sort(v[i].begin(), v[i].end()); | |
sort(inv[i].begin(), inv[i].end()); | |
vector<ii> r; | |
set_difference(v[i].begin(), v[i].end(), inv[i].begin(), inv[i].end(), | |
back_inserter(r)); | |
v[i] = r; | |
} | |
dijkstra(ini); | |
if (d[fim] == inf) cout << -1 << "\n"; | |
else cout << d[fim] << "\n"; | |
} | |
} |