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 如果在某一次迭代中,鬆弛操作沒有被執行,則說明這次迭代所有的邊都沒有被鬆弛,表示任意兩點之間在之後的迭代中沒有可能...