第k短路(Dijkstra & A*)
最短路,即第1短路有很多种求法,SPFA,Dijkstra等,但第k短路怎么求呢?其实也是基于Dijkstra;因为Dijkstra用的是堆优化,这样保证每次弹出来的都是最小值,只是求最短路只是弹出一次就返回了,我们可以用Dijkstra弹出k个距离后再返回,这样根据弹出的先后顺序能够求出1~k短路
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN = 5e5 + 100; const int MAXM = 3e3 + 10; inline int read() { int x = 0, ff = 1; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x * ff; } inline void write(ll x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } int n, m, k, v; ll ans, dist[110]; int lin[MAXN], tot = 0; struct edge { int y, v, next; }e[MAXN]; inline void add(int xx, int yy, int vv) { e[++tot].y = yy; e[tot].v = vv; e[tot].next = lin[xx]; lin[xx] = tot; } void Dijkstra() { priority_queue < pair < int , int > > q; q.push(make_pair(0, 1)); while(!q.empty()) { int x = q.top().second; int d = -q.top().first; q.pop(); if(x == n) { dist[++v] = d; if(v == k + 1) return ; } for(int i = lin[x], y; i; i = e[i].next) { y = e[i].y; ans = d + e[i].v; q.push(make_pair(-ans, y)); } } } int main() { memset(dist, -1, sizeof(dist)); n = read(); m = read(); k = read(); for(int i = 1; i <= m; ++i) { int x,y,v; x = read(); y = read(); v = read(); add(x, y, v); } Dijkstra(); for(int i = 1; i <= k; ++i) { write(dist[i]); putchar('\n'); } return 0; }
emmmmm, 还有一种更高级的算法, 先来回顾优先队列的BFS, 不断从堆中取出“当前代价最小” 的状态进行拓展。每个状态第一次从堆中取出时, 就得到了从初态到该状态的最小代价。然而, 一个状态当前最小, 不代表从该状态到目标状态代价就最小,但是优先队列BFS会先选择这个分支, 导致搜索量增大;
所以, 我们能够想到, 设计一个函数, 计算从该状态到目标状态的代价的估计值, 仍然维护一个堆,把当前价值 + 估计价值最小作为拓展;估价函数原则是估价值 <- 实际值(不再证明。。。)
在求第k短路时, 我们把最短路作为估价, 保证估价 <= 实际, 还能顺应实际的变化趋势
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 0x3f3f3f3f; const int MAXN = 5e5 + 100; const int MAXM = 3e3 + 10; template < typename T > inline void read(T &x) { x = 0; T ff = 1, ch = getchar(); while(!isdigit(ch)) { if(ch == '-') ff = -1; ch = getchar(); } while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } x *= ff; } template < typename T > inline void write(T x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } int n, m, s, t, k; int f[MAXN], vis[MAXN]; int lin[MAXN], tot = 0, linc[MAXN], totc = 0; struct edge { int y, v, next; }a[MAXN], e[MAXN]; struct node { int pos, f, dis; bool operator < (node a) const { return a.f + a.dis < f + dis; } }; inline void add(int xx, int yy, int vv) { a[++tot].y = yy; a[tot].v = vv; a[tot].next = lin[xx]; lin[xx] = tot; } inline void addc(int xx, int yy, int vv) { e[++totc].y = yy; e[totc].v = vv; e[totc].next = linc[xx]; linc[xx] = totc; } void SPFA() { queue < int > q; memset(f, 0x3f, sizeof(f)); memset(vis, false, sizeof(vis)); q.push(t); f[t] = 0; vis[t] = true; while(!q.empty()) { int x = q.front(); q.pop(); vis[x] = false; for(int i = lin[x], y; i; i = a[i].next) { if(f[y = a[i].y] > f[x] + a[i].v) { f[y] = f[x] + a[i].v; if(!vis[y]) { vis[y] = true; q.push(y); } } } } } int astar() { priority_queue < node > q; if(f[s] == INF) return -1; int ts[MAXN]; node tmp, h; h.pos = s; h.f = 0; h.dis = 0; q.push(h); while(!q.empty()) { node x = q.top(); q.pop(); ts[x.pos]++; if(ts[x.pos] == k && x.pos == t) return x.dis; if(ts[x.pos] > k) continue; for(int i = linc[x.pos]; i; i = e[i].next) { tmp.pos = e[i].y; tmp.f = f[e[i].y]; tmp.dis = x.dis + e[i].v; q.push(tmp); } } return -1; } int main() { read(n); read(m); for(int i = 1; i <= m; ++i) { int u, v, w; read(u); read(v); read(w); add(v, u, w); addc(u, v, w); } read(s); read(t); read(k); if(s == t) ++k; SPFA(); write(astar()); return 0; }