1. 程式人生 > >bellman-ford演算法 最短路

bellman-ford演算法 最短路

重要應用:在負權的圖的單源最短路問題

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;
}