单源最短路: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;
}

posted on 2018-01-08 17:44  willaty  阅读(2249)  评论(1编辑  收藏  举报

导航