Bellman-Ford 可解決帶有負權邊的最短路問題
解決負權邊和Dijkstra相比是一個優點,Bellman-Ford的核心代碼只有4行::
u[],v[],w[] 分別存一條邊的頂點、權值,dis[]存從 1 源點到各個頂點的距離
for(i=1;i<=n-1;i++) for(j=1;j<=m;j++) if(dis[v[j]] > dis[u[j]]+w[j]) dis[v[j]] = dis[u[j]]+w[j];
願過程:
循環n-1次,把每個頂點每條邊都松弛;
優化方法:
①,最壞的情況就是循環了n-1次才求出到每個頂點的最短路徑,若果在n-1次之前就已經全部松弛完成,那么后面的循環就是多余
優化:
for(k=1;k<=n-1;k++)//共有 n 個頂點,循環n-1次即可 { flag = 0; for(i=1;i<=m;i++)//對當前所有的邊進行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; flag = 1; } } if(flag == 0) break;//松弛也完成,結束 }
②,原過程:每次循環松弛過后,都有已經確定了的源點到某點最短的路徑,此后這些頂點的最短路的值就會一直保持不變,不再受后續松弛操作的影響,但是每次還要判斷是否需要松弛,這里浪費了時間。
優化:確定一條邊后總邊數減一,把不能進行本次松弛的邊再次后頭存到原數組,松弛成功的邊舍棄,再次松弛時只對未松弛的邊進行操作,m的值會隨着松弛預越來越小,直到全部完成。
for(k=1;k<=n-1;k++)//共有 n 個頂點,循環n-1次即可 { m = M;//重新賦值后的 m 是數組中存儲邊的條數 s = 1;flag = 0; for(i=1;i<=m;i++)//對當前所有的邊進行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; M--; //松弛成功,邊數減一 flag = 1; } else//把本次不能進行松弛的邊重新存儲到當前的數組 { u[s] = u[i]; v[s] = v[i]; w[s] = w[i]; s++; } } if(flag == 0) break;//松弛也完成,結束 }
附完整代碼:
#include <stdio.h> int main() { int dis[10],i,k,m,n,s=1,u[10],v[10],w[10],M,flag; int inf = 99999999; scanf("%d%d",&n,&m); M = m; for(i=1;i<=m;i++) { scanf("%d%d%d",&u[i],&v[i],&w[i]);//輸入各邊及權值 } for(i=1;i<=n;i++) { dis[i] = inf;//初始化為正無窮 } dis[1] = 0;//以 1 為源點 for(k=1;k<=n-1;k++)//共有 n 個頂點,循環n-1次即可 { m = M;//重新賦值后的 m 是數組中存儲邊的條數 s = 1;flag = 0; for(i=1;i<=m;i++)//對當前所有的邊進行松弛 { if(dis[v[i]] > dis[u[i]]+w[i]) { dis[v[i]] = dis[u[i]]+w[i]; M--; //松弛成功,邊數減一 flag = 1; } else//把本次不能進行松弛的邊重新存儲到當前的數組 { u[s] = u[i]; v[s] = v[i]; w[s] = w[i]; s++; } } if(flag == 0) break;//松弛也完成,結束 } for(i=1;i<=n;i++) { printf("%d ",dis[i]); } return 0; }
測試數據1:
5 5 2 3 2 1 2 -3 1 5 5 4 5 2 3 4 3
運行結果:
0 -3 -1 2 4
測試數據2:
5 7 1 2 2 1 5 10 2 3 3 2 5 7 3 4 4 4 5 5 5 3 6
運行結果:
0 2 5 9 9