单源最短路:Bellman-Ford算法 及 证明
描述:
求图中某一点到其他任一点的最短距离。
操作:
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; }