• 存在负权边的单源最短路径的原理和C++实现


    负权图
     


    此图用朴素迪氏或堆优化迪氏都会出错,floyd可以处理。

    负环图


     
    但floyd无法处理负权环,最短距离是无穷小。在环上不断循环。

    经过k条边的最短距离(可能有负权变)

    贝尔曼福特算法(bellman_ford)就是解决此问题的。

    原理

    循环k次,循环第i次时,m_vDis表示各点最多经过i-1条边的最短距离;vDis表示各点最多经过i条边的最短距离。

    核心代码

    template
    class CBellMan
    {
    public:
        CBellMan(int n, const vector>& edges,int s , int k )
        {
            m_vDis.assign(n, INF);
            m_vDis[s] = 0;
            for (int i = 1; i <= k; i++)
            {
                vector curDis = m_vDis;
                for (const auto& v : edges)
                {
                    if (INF == m_vDis[v[0]])
                    {
                        continue;
                    }
                    curDis[v[1]] = min(curDis[v[1]], m_vDis[v[0]] + v[2]);
                }
                m_vDis.swap(curDis);
            }
        }
        vector m_vDis;
    };

    测试样例

    #include
    #include
    using namespace std;


    int main()
    {
        const int INF = 1000 * 1000 * 1000;
        vector> edges = { {0,1,1},{1,2,2},{2,3,3},{3,0,-7} };
        vector> results = { {0,INF,INF,INF},{0,1,INF,INF},{0,1,3,INF},{0,1,3,6},{-1,1,3,6},{-1,0,3,6},{-1,0,2,6},{-1,0,2,5},{-2,0,2,5} };
        for (int i = 0; i < results.size(); i++)
        {
            CBellMan<> bm(4, edges, 0, i);
            for (int j = 0; j < 4; j++)
            {
                assert(bm.m_vDis[j] == results[i][j]);
            }
        }
    }


    最短路径

    最短路径就是经过“点数-1”条边的最短路径。如果没环,这些边可以到达任意点。如果有正权环和0权环,则拿掉这个环。如果负权环,则最小距离是无穷小。下面来检测负权环。循环“点数-1”后,再循环一次,如果有点的最短距离变小,则一定有负权环;没负权环,不会变短。如果有负权环,则再循环一次,一定有点(任意负权环的负权边的终点)距离变短。假定此点是e,拿掉负权环上所有的边后,源点到e的最短路径为Path。不拿掉负权环,则e的最短路径为:Path+此负权环。

    核心代码

    template
    class CBellMan
    {
    public:
        CBellMan(int n, const vector>& edges,int s , int k )
        {
            m_vDis.assign(n, INF);
            m_vDis[s] = 0;
            for (int i = 1; i <= k; i++)
            {
                vector curDis = m_vDis;
                Do(edges, curDis);
                m_vDis.swap(curDis);
            }
        }
        bool Check(const vector>& edges)
        {
            vector curDis = m_vDis;
            Do(edges, curDis);
            for (int i = 0; i < curDis.size(); i++)
            {
                if (m_vDis[i] != curDis[i])
                {
                    return true;
                }
            }
            return false;
        }
        void Do(const std::vector>& edges, std::vector& curDis)
        {
            for (const auto& v : edges)
            {
                if (INF == m_vDis[v[0]])
                {
                    continue;
                }
                curDis[v[1]] = min(curDis[v[1]], m_vDis[v[0]] + v[2]);
            }
        }
        vector m_vDis;
    };


    测试样例

    #include
    #include
    #include "BellMan.h"
    using namespace std;


    void Test1()
    {
        const int INF = 1000 * 1000 * 1000;
        vector> edges = { { 0,1,1 },{ 1,2,2 },{ 2,3,3 },{ 3,0,-7 } };
        vector> results = { { 0,INF,INF,INF },{ 0,1,INF,INF },{ 0,1,3,INF },{ 0,1,3,6 },{ -1,1,3,6 },{ -1,0,3,6 },{ -1,0,2,6 },{ -1,0,2,5 },{ -2,0,2,5 } };
        for (int i = 0; i < results.size(); i++)
        {
            CBellMan<> bm(4, edges, 0, i);
            for (int j = 0; j < 4; j++)
            {
                assert(bm.m_vDis[j] == results[i][j]);
            }
        }
    }

    void Test2()
    {
        const int INF = 1000 * 1000 * 1000;
        vector> edges = { { 0,1,1 },{ 1,2,2 },{ 2,3,3 },{ 3,0,-7 } };
        vector results = { false,false,true };
        for (int i = 0; i < 3; i++)
        {
            edges[3][2] = -5 - i;
            CBellMan<> bm(4, edges, 0, 3);
            assert(results[i] == bm.Check(edges));
        }
    }
    int main()
    {
        Test1();
        Test2();
    }
     

    相关下载

    源码及测试用例
    https://download.csdn.net/download/he_zhidan/88393784
     

    https://img-blog.csdnimg.cn/ea2601b3918f4aef836b5fe30da2ebf7.gif#pic_center#pic_center

    其它

    视频课程

    基础算法的C++实现课程,请点击下面的CSDN学院的链接。

    https://edu.csdn.net/course/detail/38771

    我还做了其它课程,比如:C++入职培训,C#入职培训。

    https://edu.csdn.net/lecturer/6176

    运行验证环境

    Win10 VS2022 Ck++17 或win7 VS2019 C++17

    相关下载

    如果你时间宝贵,只想看精华,请到CSDN下载频道下载《闻缺陷则喜算法册》doc版

    https://download.csdn.net/download/he_zhidan/88348653

    博主有几句话想对大家说

    算法是程序的灵魂,没有好的算法,程序就死气沉沉。

    问题发现得越早,越给老板省钱。我简称为:闻缺陷则喜。

    有所得,以墨记之,故曰墨家

    https://img-blog.csdnimg.cn/f95ddae62a4e43a68295601c723f92fb.gif#pic_center

  • 相关阅读:
    第一次pta认证P测试C++
    RAR压缩包如何加密,忘记密码如何找回?
    SpringMVC 02
    Python shutil
    alias linux 命令别名使用
    一文快速上手 Nacos 注册中心+配置中心!
    《洛谷深入浅出进阶篇》 P1496火烧赤壁——初识离散化
    Android学习笔记 57. 了解API级别和兼容性
    Python数据结构:列表(list)、元组(tuple)、字典(dict)
    java计算机毕业设计ssm大学生心理健康平台(源码+系统+mysql数据库+Lw文档)
  • 原文地址:https://blog.csdn.net/he_zhidan/article/details/133551398