bellman-ford演算法 最短路
阿新 • • 發佈:2019-02-18
重要應用:在負權的圖的單源最短路問題
Bellman-Ford 演算法和 Dijkstra 演算法都是可以解決單源最短路徑的演算法,一個實現的很好的 Dijkstra 演算法比 Bellman-Ford 演算法的執行時間要低,但dijkstra演算法無法解決存在負權環的圖的單源最短路問題(因為dijkstra演算法每迴圈一次,確定一條到某個頂點的最短路,之所以能確定,是因為每條邊都是正值,若到達其他頂點的最短路經過該頂點,則最短路總權值會更大)。
Bellman-Ford 演算法描述:
如求下圖單源最短路徑,以1為源點,求到各頂點的最短路徑,黑色代表權值,紅色代表第幾條邊(是第幾條邊在實際中會不同,這裡是假設 )
1.初始化:
將除源點外的所有頂點的最短距離估計值 d[v] =+∞,源頂點距離為 0;
2.迭代求解計算最短路徑:
執行 V - 1 次 (遍歷一條最短路徑經過的點最多是v-1個嘛,所有鬆弛|v|-1次後最短路必然會出現!);
反覆對邊集e中的每條邊進行鬆弛操作,(即如果起點 u 的距離 d 加上邊的權值 w 小於終點 v 的距離 d,則更新終點 v 的距離值 d),使得頂點集V中的每個頂點v的最短距離估計值逐步逼近其最短距離;
第一次迴圈:
從第一條邊開始到第十條邊進行鬆弛操作;
得到下圖:
第二次迴圈:
也是從第一條邊開始到第十條邊進行鬆弛操作;與上面同理;
得到如圖;
第三次迴圈:
也是從第一條邊開始到第十條邊進行鬆弛操作;與上面同理;
因為第三次迴圈發現任何沒有任何一個更新,說明到各個頂點的最短路都已經找到,break;跳出迴圈,結束
3.檢驗負權迴路:
遍歷圖中的所有邊,計算 u 至 v 的距離,如果對於 v 存在更小的距離,則說明存在環(因為經過上面步驟二, 如果不存在負環,則到各點的最短路已經確定);
for(i=1;i<=enum;i++) //檢查有無負權環
{
if(d[edge[i].v]>dis[e[i].u]+edge[i].w)
{
flag = false ;//存在負環
break;
}
}
程式碼:無路徑
#include<stdio.h>
#include<string.h>
#define Max 99999
int m,n,d[10000];//n條邊
struct Edge{
int u;
int v;
int w;
}e[10000];
bellmenford(int root){
int i,j,k;
for(i=1;i<=m;i++){
d[i]=Max;
}
d[root]=0;
for(k=0;k<m-1;k++){//迴圈k-1次
for(i=1;i<=n;i++){//迴圈每條邊
if(d[e[i].v]>d[e[i].u]+e[i].w){//如果起點 u 的距離 d 加上邊的權值 w 小於終點 v 的距離 d,則更新終點 v 的距離值 d
d[e[i].v]=d[e[i].u]+e[i].w;
}
}
}
for(i=1;i<=n;i++){//判斷負環
if(d[e[i].v]>d[e[i].u]+e[i].w){
printf("NO");
return 0;
}
}
for(i=1;i<=m;i++){
printf("%d ",d[i]);
}
}
int main(){
int i,j,k=1;
scanf("%d%d",&m,&n);
for(i=1;i<=n;i++){
scanf("%d%d%d",&e[k].u,&e[k].v,&e[k].w);
k++;
}
bellmenford(1);
return 0;
}
有路徑輸出:
#include<stdio.h>
#include<string.h>
#define Max 99999
int m,n,d[10000],pre[10000];//n條邊,d[]表示到某頂點的總權值,pre[]記錄路徑
struct Edge{
int u;
int v;
int w;
}e[10000];
bellmenford(int root){
int i,j,k;
for(i=1;i<=m;i++){
d[i]=Max;
pre[i]=root;//初始化到各頂點的路徑的起點為源點
}
d[root]=0;
for(k=0;k<m-1;k++){//迴圈k-1次
for(i=1;i<=n;i++){//迴圈每條邊
if(d[e[i].v]>d[e[i].u]+e[i].w){//如果起點 u 的距離 d 加上邊的權值 w 小於終點 v 的距離 d,則更新終點 v 的距離值 d
d[e[i].v]=d[e[i].u]+e[i].w;
pre[e[i].v]=e[i].u;//到v點經過u點
}
}
}
for(i=1;i<=n;i++){//判斷負環
if(d[e[i].v]>d[e[i].u]+e[i].w){
printf("NO");
return 0;
}
}
for(i=1;i<=m;i++){
printf("%d %d ",d[i],i);
j=i;
while(pre[j]!=root){
printf("%d ",pre[j]);
j=pre[j];
}
if(i!=root)printf("%d",root);
printf("\n");
}
}
int main(){
int i,j,k=1;
scanf("%d%d",&m,&n);
for(i=1;i<=n;i++){
scanf("%d%d%d",&e[k].u,&e[k].v,&e[k].w);
k++;
}
bellmenford(1);
return 0;
}