1. 程式人生 > >圖論(三) (一)最短路徑問題 Bellman-Ford算法

圖論(三) (一)最短路徑問題 Bellman-Ford算法

描述 a算法 演變 jks truct 再次 算法理解 理解 std

簡要:Bellman-Ford算法計算的仍然是從一個點到其他所有點的最短路徑算法,其時間復雜度是O(NE),N表示點數,E表示邊數,不難看出,當一個圖稍微稠密一點,邊的數量會超過點數那麽實際上效率是低於Dijkstra算法的。但是本算法可以計算存在負權邊的情況(不存在負回路),因此可以用於更廣泛的情況,但其實在日常解題應用中我們基本不會用到該算法,因為有一個比它效率更高的算法即SPFA算法,下一章會介紹到。SPFA算法其實是從Bellman-Ford算法演變而來的,那麽從基礎的開始,我們先來理解一下Bellman-Ford算法。

算法描述:s為起點,dis[v]為s到v的最短距離,pre[v]是v的前驅結點,w[j]是邊 j 的長度,j 邊的起點和終點分別是u,v。

1、初始化:dis[s]=0, dis[v]=∞(v≠s), pre[s]=0

2、for(i=1;i<=n-1;i++)

for(j=1;j<=e;j++)

if(dis[u]+w[j]<dis[v]){

dis[v]=dis[u]+w[j];

pre[v]=u;

}

算法理解:一開始已標記的點只有起點,每一次枚舉所有的邊,總會有一些邊連接著已標記的點和未標記的點,即已經計算過最短距離和為計算過最短距離的點。因此每次枚舉都會更新一些未標記的點成為已標記的點。而n-1次則保證了最壞情況下所有的點均可以被標記,即圖的樣子是一“串”的情況。

對於負權回路的情況,因為每一次枚舉都會走過一圈負權回路,那麽恰好在該回路兩邊的點之間的最短距離就會無限減小,因此會造成錯誤。對此,大家給出了一個不是解決方法的解決方法,就是如果在兩重循環結束後,再次枚舉每條邊,如果再次出現某兩點的距離減少,那麽就返回"錯誤",已表示有負權回路,無法算出答案。

當然,對於負權回路也有解決方法,在介紹完SPFA算法後會補充一下。

代碼如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int n,e,s;
struct node{
    int x;
    
int y; int val; }m[105]; int dis[105],pre[105],a,b,w[105][105]; int bellmanford(int s){ int i,j; for(i=1;i<=n;i++) dis[i]=w[s][i]; dis[s]=0;pre[s]=0; for(i=1;i<=n-1;i++) for(j=1;j<=e;j++) if(dis[m[j].x]+m[j].val<dis[m[j].y]) { dis[m[j].y]=dis[m[j].x]+m[j].val; pre[m[j].y]=m[j].x; } for(j=1;j<=e;j++){ if(dis[m[j].x]+m[j].val<dis[m[j].y]) return 0; } return dis[5]; } int main(){ int i,j; scanf("%d%d",&n,&e); memset(dis,100,sizeof(dis)); memset(w,100,sizeof(w)); for(i=1;i<=e;i++){ scanf("%d%d%d",&m[i].x,&m[i].y,&m[i].val); a=m[i].x; b=m[i].y; w[a][b]=m[i].val; } for(i=1;i<=5;i++) for(j=1;j<=5;j++) printf("%d ",w[i][j]); scanf("%d",&s); printf("%d",bellmanford(s)); return 0; }

圖論(三) (一)最短路徑問題 Bellman-Ford算法