1. 程式人生 > >Bellman_ford算法詳解

Bellman_ford算法詳解

space clu [0 編號 mes 優化 很好 == tin

昨天說的dijkstra固然很好用,但是卻解決不了負權邊,想要解決這個問題,就要用到Bellman-ford.

我個人認為Bellman-Ford比dijkstra要好理解一些,還是先上數據(有向圖):

7
2 8
3 5
3 -6
4 -3
4 7
5 -2
5 -3

在講述開,先設幾個數組:

origin[i]表示編號為i這條邊的起點編號,如origin[4]=2

destination[i]表示編號為i這條邊的終點編號,如origin[5]=5

value[i]表示編號為i這條邊的權值,如value[3]=-6

dis[i],和昨天一樣,源點到i號點的估計距離,經過不斷更新會變成時機距離,就是答案。

bellmanford的實際意義就是掃描一條邊,看如果走這條邊能不能使這條邊的dis[destination[i]],變少,現在我來模擬一下:

初始的dis:[0,∞,∞,∞,∞]

首先從第一條邊1 2 8開始,判斷走這條邊能不能使這條邊的終點的dis變短,原本dis[2]=∞,而dis[1]=0,而這條邊的權值:value[1]=8,0+8<∞所以將dis[2]更新成8.

dis[0,8,∞,∞,∞]

然後是第二條邊,用剛才的方法將dis[3]從∞更新成5.

dis[0,8,5,∞,∞]

第三條2 3 -8,原本的dis[3]=5,如果走第三條邊,則dis[3]=dis[2]+value[3]=8+(-6)=2<5,所以dis[3]更新成2.

dis[0,8,2,∞,∞]

以此類推,經過第一輪更新,dis數組如下:

dis[0,8,2,15,0]

但是第一次更新後,並不是最優解於是開始第二次更新。

按照第一次更新的步驟一步一步來得到的答案是

dis[0,8,2,-3,0]

這便是最優解,但是問題來了,一般要更新多少次呢?

n-1次。這樣能保證更新出的一定是最優解。

好了,呈上代碼:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include 
<cstdlib> using namespace std; int dis[10010]; int origin[10010],destination[10010],value[10010];//剛剛說過的三個數組 int n,m; void Bellman_ford(int a) { memset(dis,88,sizeof(dis));//賦初始值 dis[a]=0; for(int i=1;i<=n-1;i++)//更新n-1次 for(int j=1;j<=m;j++)//更新每一條邊 dis[destination[j]]=min(dis[destination[j]],dis[origin[j]]+value[j]);//判斷是否更新 } int main() { cin>>n>>m; for(int i=1;i<=m;i++) cin>>origin[i]>>destination[i]>>value[i]; Bellman_ford(1); for(int i=1;i<=n;i++) cout<<dis[i]<<" "; }

有些人可能發現了,很多時候實際上不用更新n-1次,因此我們可以用隊列優化:

每次選出隊首點,對與隊首點鏈接的所有點的dis進行更新,並加入隊列,然後隊首點pop出隊列,

這個算法最好用鄰接表實現,代碼如下:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
using namespace std;
int dis[10010];
int book[10010];
int origin[10010],destination[10010],value[10010];
int n,m;
int total;
int next[10010],head[10010];
void adl(int a,int b,int c)//鄰接表
{
   total++;
   origin[total]=a;
   destination[total]=b;
   value[total]=c;
   next[total]=head[a];
   head[a]=total;
}
void Bellman_ford(int a)
{
    memset(book,0,sizeof(book));//book[i]表示i號點是否在隊列裏
    memset(dis,88,sizeof(dis));
    queue <int> q;
    q.push(a);
    book[a]=1;
    dis[a]=0;
    while(!q.empty())//當隊列不為空時更新
    {
        for(int e=head[q.front()];e;e=next[e])//枚舉隊首點相鄰的每一個點
        {
            if(dis[destination[e]]>dis[origin[e]]+value[e])
            {
                dis[destination[e]]=dis[origin[e]]+value[e];
                if(book[destination[e]]==0)
                {
                    q.push(destination[e]);//將更新的這一個點入隊
                    book[destination[e]]=1;
                }
            }
        }
        q.pop();//彈出隊首元素
    }
 } 
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        adl(a,b,c);
   } 
    Bellman_ford(1);
    for(int i=1;i<=n;i++)
        cout<<dis[i]<<" "; 
}

總結一下,bellman_ford的空間復雜度是m時間復雜度是O(nm),經過隊列優化,時間復雜度是<=O(nm)。

Bellman_ford算法詳解