描述:
求圖中某一點到其他任一點的最短距離。
操作:
1. 初始化
結果保存在一個dist數組里,源點的結果初始化為0,其他初始化為無窮大(如INT32_MAX)。
2. 計算:
兩重for循環,第一層,迭代n - 1次(n為節點數);
第二層,遍歷每條邊,如果其源點對應的距離加上邊權重小於終點對應距離,則更新終點最短距離;
3. 判斷負權環:
當步驟二計算完時,遍歷左右邊,看是否存在某條邊(u, v),有d[u] + w(u, v) < d[v],即還可以更新,如存在則存在負權環。
證明:
求最短路證明:
一。假設某點與源點不連通。
由於初始化時,除了源點距離初始為0之外,其他點都初始化為無窮大,如果不連通,則某點所在的連通圖的任一條邊都不會導致更新。
二。假設x點與源點連通。
每個點都存在自己的最短路,為(e0, e1, e2, ..., ek)。
顯然,源點只要經過n - 1條邊就可到達任一點; (一)
現只需證明,對x點,每次迭代(松弛),至少有一條最短邊ei的距離被找到,除非已經到達x點。 (二)
對於第一次迭代,必定更新和源點相連的所有邊(如果是有向圖,則是指出去的),由於源點距離是0,和其相連的都是無窮大。
而這些往外連的邊中,必有一條是x點的最短路上起始的一條邊。
則設有點k,這個點是x最短路上的一個點,下一次松弛,必能找到下一個點,且也是x的最短路上的一個點:
由於下一次松弛將更新與k相連的所有點。
有(一)(二)可得,上面兩次迭代可以找出最短路。
而關於負權環的判斷:
由於負權環的存在,可以通過不斷繞着走從而減小環上各個點的距離。
所以即使迭代完成,當判斷是否還能更新時,會發現還是可以更新的,這就判斷了存在負權環。
代碼:
簡單輸出最短路,如果要路徑可以為每個節點儲存一個最后一次訪問的節點,逆向遍歷一遍即可得到路徑。
#include <string> #include <iostream> #include <vector> using namespace std; struct Edge { int start; int end; int weight; }; bool BellmanFord() { int source = 0; int vex_num, edge_num; cout << "Input the number of vertexs and edges:" << endl; cin >> vex_num >> edge_num; vector<int> dist(vex_num, INT32_MAX); dist[source] = 0; vector<Edge> e; for (int i = 0; i < edge_num; i++) { Edge et; cin >> et.start >> et.end >> et.weight; e.push_back(et); } for (int i = 0; i < vex_num - 1; i++) for (int j = 0; j < edge_num; j++) if (dist[e[j].start] + e[j].weight < dist[e[j].end] && dist[e[j].start] != INT32_MAX) dist[e[j].end] = dist[e[j].start] + e[j].weight; for (int i = 0; i < dist.size(); dist++) cout << dist[i] << endl; }