BellMan-ford算法描述
1.初始化:將除源點外的所有頂點的最短距離估計值 dist[v] ← +∞, dist[s] ←0;
2.迭代求解:反復對邊集E中的每條邊進行松弛操作,使得頂點集V中的每個頂點v的最短距離估計值逐步逼近其最短距離;(運行|v|-1次)
3.檢驗負權回路:判斷邊集E中的每一條邊的兩個端點是否收斂。如果存在未收斂的頂點,則算法返回false,表明問題無解;否則算法返回true,並且從源點可達的頂點v的最短距離保存在 dist[v]中。
舉例說明:
給定原點是s,初始化時候除了原點s之外,其他的都是無窮大的。因為有5個頂點,所以需要松弛的次數為5-1次
這里我們按照邊<t,x>、<t,y>、<t,z>、<y,x>、<y,z>、<z,x>、<z,s>、<s,t>、<s,y>的順序進行變得松弛操作。
第一次按照上述邊進行松弛操作之后(實際上只對<s,t>、<s,y>進行操作)的結果為
第二次按照給定邊進行松弛操作之后:
第三次松弛操作之后:
最后一次松弛操作:
給出實力代碼

1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 5 #define INF 0xffff //權值上限 6 #define maxe 5000 //邊數上限 7 #define maxn 100 //頂點數上限 8 int n, m; //頂點數、邊數 9 int d[maxn]; //保存最短路徑權值的數組 10 int parent[maxn]; //每個頂點的前驅頂點,用以還原最短路徑樹 11 struct edge //表述邊的結構體,因為要對每一條邊松弛 12 { 13 int u, v, w; //u為邊起點,v為邊端點,w為邊權值,可以為負 14 }EG[maxe]; 15 16 bool Bellman_Ford(int s) //計算從起點到所有頂點的 17 { 18 for(int i = 1; i <= n; i++) //初始化操作d[EG[j].v] > d[EG[j].u]+EG[j].w 19 { 20 d[i] = INF; 21 parent[i] = -1; 22 } 23 d[s] = 0; 24 bool flag; //標記,判斷d值是否更新,跳出外層循環的依據 25 for(int i = 1; i < n; i++) //外層循環最多做n-1次 26 { 27 flag = false; //初始為false,假設不需再更新 28 for(int j = 0; j < m; j++) //對m條邊進行松弛操作,若有更新,flag記為true 29 if(d[EG[j].v] > d[EG[j].u]+EG[j].w) //if d[v] > d[u] + w(u, v),更新d[v] 30 { 31 d[EG[j].v] = d[EG[j].u]+EG[j].w; 32 parent[EG[j].v] = EG[j].u; 33 flag = true; 34 } 35 if(!flag) break; //若松弛完每條邊后,flag狀態不變,說明未發現更新,可直接跳出循環 36 } 37 for(int i = 0; i < m; i++) //做完上述松弛后,如果還能松弛,說明存在負權回路,返回false 38 if(d[EG[i].v] > d[EG[i].u]+EG[i].w) 39 return false; 40 return true; //不存在負權回路,返回true 41 } 42 43 int main() 44 { 45 int st; 46 printf("請輸入n和m:\n"); 47 scanf("%d%d", &n, &m); 48 printf("請輸入m條邊(u, v, w):\n"); 49 for(int i = 0; i < m; i++) 50 scanf("%d%d%d", &EG[i].u, &EG[i].v, &EG[i].w); 51 printf("請輸入起點:"); 52 scanf("%d", &st); 53 if(Bellman_Ford(st)) 54 { 55 printf("不存在負權回路。\n"); 56 printf("源頂點到各頂點的最短路徑權值為:\n"); 57 for(int i = 1; i <= n; i++) 58 printf("%d ", d[i]); 59 printf("\n"); 60 } 61 }
輸入測試用例
1 2 -1
1 3 4
2 3 3
2 4 2
2 5 2
4 2 1
4 3 5
5 4 -3
Dijkstra算法
(1)該算法要求所有邊的權重均為非負值,即對於所有的邊(u,v)∈E,ω(u,v)≥0,—— 既不能有負權重的邊,更 不能 有負權重的環。算法在運行過程中維持的關鍵信息是一組結點集合S。從源結點s到該集合中每個節點之間的最短路徑已經被找到。算法重復的從結點集合V-S中選擇最短路徑估計最小的結點u,將u加入到集合S中,然后對所有從u發出的邊進行松弛操作。
(2)算法設計思想如下:
(3)算法描述偽代碼如下:
(4)下面給出算法一些理解:
上述算法第一行執行的是d值和π值的初始化,第2行將集合S初始化為一個空集。算法所維持的循環不變式為Q=V-S,不變式在算法的while循環中保持不變。
第3行對最小優先隊列進行初始化,將所有的結點V都放在隊列里面。此時的S=∅。
在執行while循環的時候,從集合Q=V-S中抽取結點u,第6行將該結點加入到結合S里面,從而保持不變式成立。
然后將從u結點出發的所有結點進行松弛操作。如果一條經過結點u的路徑能夠使得從源結點s到結點v的最短路徑權重比當前的估計值更小,就對v.d的數值和前驅結點進行更新操作。
(5)下面是一個實例
輸入G,源結點1,結點集合V=<1,2,3,4,5,6>
①從源結點開始,此時集合S中只有結點1,從1出發的結點有6、2,所以更新此時的dist[2]和dist[6]的數值
②選擇dist[2]和dist[6]中最小的值,即dist[6]=3,將結點6加入到集合S中,然后從更新結點6出發的結點2、5、4的路徑值,分別更新為dist[2] = 5、dist[5] = 4、dist[4] = 9。
③選擇dist[2] = 5、dist[5] = 4、dist[4] = 9中最小的值,即dist[5] = 4,將結點5加入到結合S中,然后更新從結點5出發的結點的值,由於沒有從結點5出發的結點,所以直接進行下一步
④第三步中選擇dist[2] = 5,將結點2加入集合S中,然后更新從結點2出發的結點3、4的值,分別為dist[3] = 12、dist[4] = 9
⑤從剩下的兩個結點3、4中選擇dist更小的結點4,將結點4加入集合S中,然后更新從結點4出發的結點3的值,判斷之后,發現dist[3]依舊還是12
⑥最后一步,將結點3加入集合S中,此時以及更新完畢,找到了從結點1到達所有節點的最短路徑