模板-Bellman-Ford&SPFA
阿新 • • 發佈:2020-08-17
Bellman-Ford演算法
求最短路的
這個演算法基於一個叫做“鬆弛”的操作
鬆弛會試圖往最短路中加邊來縮短最短路
對於這樣一個圖
1到3的最短路顯然是1→2→3而不是1→3繞遠路就是最短的捷徑
我們所進行的鬆弛操作就是這樣的
鬆弛時列舉每一條邊,並判斷先走最短路到達這條邊的u點,再經過這條邊到達v點,能否使到v點的最短路縮小
若可以,就將到v點的最短路更新
顯然,只用一次鬆弛不能夠得到完整的最短路,所以要進行多次鬆弛
經過一大堆亂七八糟的玄學證明,我們知道只需要進行n-1次鬆弛即可
所以,當n=1時,我們只要進行0次鬆弛即可
n=2時,1次即可
n=3,3次即可
n=2147483647,2147483646即可
不行這個數太大了
實際上我們發現,n-1是最大次數
實際上有效鬆弛有可能沒有這麼多
所以我們判斷如果這次鬆弛沒有讓最短路發生變化,就直接結束演算法
對於負權環,由於正常情況下最多進行n-1次有效鬆弛,如果在n-1次全部完成後還可以鬆弛,就可以說明這裡面有負權環了
程式碼:
#include <bits/stdc++.h> using namespace std; int n,m; struct Edge { int u,v,w,nxt; }e[10]; int h[10],cnt; int dis[10]; int bak[10]; void add(int u,int v,int w) { e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=h[u]; h[u]=cnt; } int main() { cin >> n >> m; for(int i=1;i<=m;i++) { int a,b,c; cin >> a >> b >> c; add(a,b,c); } for(int i=1;i<=n;i++) dis[i]=0x3f; dis[1]=0; for(int k=1;k<=n-1;k++) { for(int i=1;i<=n;i++) bak[i]=dis[i]; for(int i=1;i<=m;i++) if(dis[e[i].v]>dis[e[i].u]+e[i].w) dis[e[i].v]=dis[e[i].u]+e[i].w; int check=0; for(int i=1;i<=n;i++) if(bak[i]!=dis[i]) { check=1; break; } if(!check) break; } int flag=0; for(int i=1;i<=m;i++) if(dis[e[i].v]>dis[e[i].u]+e[i].w) flag=1; if(flag==1) cout << "無最短路" << endl; else { for(int i=1;i<=n;i++) cout << dis[i] << ' '; } return 0; }
但是我們回頭看看這個程式碼
鬆弛時列舉每一條邊
每一條
根據我的經驗,我懷疑這裡面有可能有許多沒用的鬆弛
實際上我們可以通過一大堆亂七八糟的玄學證明,鬆弛只有可能令一部分點的最短路縮小
這一部分點就是鬆弛時所加入的那條邊的v點所連著的點
於是我們就接觸到了另一個演算法,另一個在Bellman-Ford演算法基礎上的最短路演算法——
Shortest Path Faster Algorithm
它利用佇列,每次取出隊首,列舉隊首所有的邊進行鬆弛
並將那些鬆弛成功的邊的v點入隊
以達到避免無效鬆弛的效果
程式碼:
#include <bits/stdc++.h> using namespace std; int n,m; struct Edge { int u,v,w,nxt=-1; }e[10]; int h[10],cnt; int dis[10]; int bak[10]; void add(int u,int v,int w) { e[++cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].nxt=h[u]; h[u]=cnt; } queue<int> q; int bk[10]; int main() { cin >> n >> m; for(int i=1;i<=m;i++) { int a,b,c; cin >> a >> b >> c; add(a,b,c); } for(int i=1;i<=n;i++) dis[i]=999999; dis[1]=0; for(int i=1;i<=n;i++) bk[i]=0; q.push(1); bk[1]=1; while(!q.empty()) { int k=h[q.front()]; while(k!=-1) { if(dis[e[k].v]>dis[e[k].u]+e[k].w) { dis[e[k].v]=dis[e[k].u]+e[k].w; if(bk[e[k].v]==0) { q.push(e[k].v); bk[e[k].v]=1; } } k=e[k].nxt; } bk[q.front()]=0; q.pop(); } for(int i=1;i<=n;i++) cout << dis[i] << ' '; return 0; }