图论-BellmanFord算法

图论-BellmanFord算法

BellmanFord算法用于计算单源多节点最短路问题,并且能够处理负权重的边和判断是否存在负环。

松弛操作

在计算每个节点v来说,我们维持一个属性v.d为源节点s到v节点最短距离的上限。称为s到v的最短路径估计,松弛操作就是减少每个节点的v.d值,也就是最短路径的上限。

void relax(int u, int v, int w)
{
    if (d[v] > d[u] + w)
    {
        d[v] = d[u] + w;
    }
}

根据上面的代码我们可以知道,松弛一条边(u,v)就是如果存在到节点v更短的路径(s,u)+(u,v),那么就更新v.d的值,很好理解。

BellmanFord算法

该算法的第一步就是初始化d值,把所有节点的d值初始化为无穷大,开始默认都无法达到,把源节点的d值初始化为0,默认开始可以达到而不需要花费权值。

    for (int i = 0; i < n; i++)
    {
        d[i] = INT32_MAX;
    }
    d[s] = 0;

接下来,我们对边进行松弛。

    for (int i = 0; i < n - 1; i++)
    {
        for (edge e : edges)
        {
            relax(e.u, e.v, e.w);
        }
    }

我们外层循环 ∣ V ∣ − 1 |V|-1 V1次,每次循环对所有的边进行松弛操作。即可得到单源多节点最短路数组d。

所有代码如下:

unsigned int d[10001];

struct edge
{
    int u;
    int v;
    int w;
};

void relax(int u, int v, int w)
{
    if (d[v] > d[u] + w)
    {
        d[v] = d[u] + w;
    }
}

void bellmanFord(int n, int s, vector<edge> &edges)
{
    for (int i = 0; i < n; i++)
    {
        d[i] = INT32_MAX;
    }
    d[s] = 0;

    for (int i = 0; i < n - 1; i++)
    {
        for (edge e : edges)
        {
            relax(e.u, e.v, e.w);
        }
    }
}

证明

假设从源节点s到某一个目标节点v,存在路径 ( v 0 , v 1 , … , v k ) (v_{0},v_{1},\dots,v_{k}) (v0,v1,,vk),其中 v 0 v_{0} v0为s, v k v_{k} vk为v,因为最短路都是简单路径,不存在环,因此这个路径最多包含图的所有节点,因此最多包含 ∣ V ∣ − 1 |V|-1 V1条边。在第一次对边的松弛操作之后,边 ( v 0 , v 1 ) (v_{0},v_{1}) (v0,v1)必定会被松弛,因此 v 1 v_{1} v1的d值就是最短路,依次类推第二次松弛必定会松弛边 ( v 1 , v 2 ) (v_{1},v_{2}) (v1,v2) v 2 v_{2} v2更新完毕,最后在第 ∣ V ∣ − 1 |V|-1 V1次松弛之后,边 ( v k − 1 , v k ) (v_{k-1},v_{k}) (vk1,vk)松弛, v k v_{k} vk的值更新完毕,因此最长的路径需要 ∣ V ∣ − 1 |V|-1 V1次松弛,所以有最外层的循环次数是 ∣ V ∣ − 1 |V|-1 V1

算法复杂度是 Θ ( V E ) \Theta(VE) Θ(VE)

负环判定

void bellmanFord(int n, int s, vector<edge> &edges)
{
    for (int i = 0; i < n; i++)
    {
        d[i] = INT32_MAX;
    }
    d[s] = 0;

    for (int i = 0; i < n - 1; i++)
    {
        for (edge e : edges)
        {
            relax(e.u, e.v, e.w);
        }
    }

    for (edge e : edges)
    {
        if(d[e.v] > d[e.u] + e.w)
        {
            return false;
        }
    }

    return true;
}

最后我们对每个边进行判断,如果不存在负环,那么根据三角不等式,没有任何的边可以使判别式为真从而返回false,当且仅当存在负环时候才会返回false。

证明可以参阅算法导论中的反证法。

改进

我们可以看到,回看上面的证明,在第一次松弛操作中,如果从边 ( v k − 1 , v k ) (v_{k-1},v_{k}) (vk1,vk)开始倒着松弛,那么只有最后一次 ( v 0 , v 1 ) (v_{0},v_{1}) (v0,v1)的松弛是有效的,其余都是无效操作,因此我们就要尽量减少这种无效操作,因此就有了队列优化:SPFA

你可能感兴趣的:(#,图论)