Bellman Ford演算法,SPFA演算法

2022-09-03 15:12:15 字數 3203 閱讀 8372

bellman-ford

演算法能在更普遍的情況下(存在負權邊)解決單源點最短路徑問題。對於給定的帶權(有向或無向)圖g=(

v,e),其源點為

s,加權函式w是

邊集e 

的對映。對圖g執行

bellman-ford

演算法的結果是乙個布林值,表明圖中是否存在著乙個從源點s

可達的負權迴路。若不存在這樣的迴路,演算法將給出從源點s到

圖g的任意頂點

v的最短路徑

d[v]

。(1)    

初始化:將除源點外的所有頂點的最短距離估計值

d[v]

←+∞, d[s] ←0;

(2)    

迭代求解:反覆對邊集e中的每條邊進行鬆弛操作,使得頂點集v中的每個頂點v的最短距離估計值逐步逼近其最短距離;(執行|v|-1次)

(3)    

檢驗負權迴路:判斷邊集

e中的每一條邊的兩個端點是否收斂。如果存在未收斂的頂點,則演算法返回

false

,表明問題無解;否則演算法返回

true

,並且從源點可達的頂點

v的最短距離儲存在

d[v]

中。演算法描述如下:

bellman-ford(g,w,s) 

:boolean//圖g

,邊集函式w,

s為源點

1        

for each vertex v

∈ v(g) do        //初始化 1階段

2d[v]

←+∞3

d[s] ←0;                             //1階段結束

4for i=1 to |v|-1 do               //2階段開始,雙重迴圈。

5for each edge(u,v) ∈e(g) do //邊集陣列要用到,窮舉每條邊。

6if d[v]> d[u]+ w(u,v) then      //鬆弛判斷

7d[v]=d[u]+w(u,v)               //鬆弛操作   2階段結束

8for each edge(u,v) ∈e(g) do

9if d[v]> d[u]+ w(u,v) then

10exit false

11    

exit true

下面給出描述性證明:

首先指出,圖的任意一條最短路徑既不能包含負權迴路,也不會包含正權迴路,因此它最多包含

|v|-1

條邊。其次,從源點

s可達的所有頂點如果

存在最短路徑,則這些最短路徑構成乙個以

s為根的最短路徑樹。

bellman-ford演算法的迭代鬆弛操作,實際上就是按頂點距離s的層次,逐層生成這棵最短路徑樹的過程。

在對每條邊進行1遍鬆弛的時候,生成了從s出發,層次至多為1的那些樹枝。也就是說,找到了與s至多有1條邊相聯的那些頂點的最短路徑;對每條邊進行第2遍鬆弛的時候,生成了第2層次的樹枝,就是說找到了經過2條邊相連的那些頂點的最短路徑……。因為最短路徑最多隻包含|v|-1 條邊,所以,只需要迴圈|v|-1 次。

每實施一次鬆弛操作,最短路徑樹上就會有一層頂點達到其最短距離,此後這層頂點的最短距離值就會一直保持不變,不再受後續鬆弛操作的影響。(但是,每次還要判斷鬆弛,這裡浪費了大量的時間,怎麼優化?單純的優化是否可行?)

如果沒有負權迴路,由於最短路徑樹的高度最多只能是|v|-1,所以最多經過|v|-1遍鬆弛操作後,所有從s可達的頂點必將求出最短距離。如果 d[v]仍保持 +∞,則表明從s到v不可達。

如果有負權迴路,那麼第 |v|-1 遍鬆弛操作仍然會成功,這時,負權回路上的頂點不會收斂。

/*

* 單源最短路演算法spfa,時間複雜度o(ke),k在一般情況下不大於2,對於每個頂點使用可以在o(ve)的時間內算出每對節點之間的最短路

* 使用了佇列,對於任意在佇列中的點連著的點進行鬆弛,同時將不在佇列中的連著的點入隊,直到隊空則演算法結束,最短路求出

* spfa是bellman-ford的優化版,可以處理有負權邊的情況

* 對於負環,我們可以證明每個點入隊次數不會超過v,所以我們可以記錄每個點的入隊次數,如果超過v則表示其出現負環,演算法結束

* 由於要對點的每一條邊進行列舉,故採用鄰接表時時間複雜度為o(ke),採用矩陣時時間複雜度為o(kv^2)

*/#include

#include

#include

#define maxv 10000

#define inf 1000000000 //此處建議不要過大或過小,過大易導致運算時溢位,過小可能會被判定為真正的距離

using std::

vector

;using std::

queue

;struct edge; 

vector e[maxv]

;//由於一般情況下e;//儲存到原點0的距離,可以開二維陣列儲存每對節點之間的距離

int cnt[maxv]

;//記錄入隊次數,超過v則退出

queue<

int> buff;

//佇列,用於儲存在spfa演算法中的需要鬆弛的節點

bool done[maxv]

;//用於判斷該節點是否已經在佇列中

int v;

//節點數

int e;

//邊數

bool spfa(

const

int st)

dist[i]

=inf;

//非原點距離無窮大

} buff.push

(st)

;//原點入隊

done[st]=1

;//標記原點已經入隊

cnt[st]=1

;//修改入隊次數為1

while

(!buff.empty()

)}}}

buff.pop()

;//彈出隊首節點

done[tmp]=0

;//將隊首節點標記為未入隊

}return

true

;//返回true

}//演算法結束

int main()if

(!spfa(0)

)else

return0;

}

Bellman ford 演算法詳解

昨天說的dijkstra固然很好用,但是卻解決不了負權邊,想要解決這個問題,就要用到bellman ford.我個人認為bellman ford比dijkstra要好理解一些,還是先上資料 有向圖 5 712 8135 23 6 5 4 324 735 2 45 3 在講述開,先設幾個陣列 orig...

Bellman Ford演算法 模板

如題,給出乙個有向圖,請輸出從某一點出發到所有點的最短路徑長度。輸入格式 第一行包含三個整數n m s,分別表示點的個數 有向邊的個數 出發點的編號。接下來m行每行包含三個整數fi gi wi,分別表示第i條有向邊的出發點 目標點和長度。輸出格式 一行,包含n個用空格分隔的整數,其中第i個整數表示從...

Bellman Ford演算法優化

2017 07 27 16 02 48 writer pprp 在bellman ford演算法中,其最外層的迴圈的迭代次數為n 1,如果不存在負權迴路,需要迭代的次數是遠遠小於n 1 如果在某一次迭代中,鬆弛操作沒有被執行,則說明這次迭代所有的邊都沒有被鬆弛,表示任意兩點之間在之後的迭代中沒有可能...