首页 > 编程语言 >存在负权边的单源最短路径的原理和C++实现

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

时间:2023-10-19 12:06:00浏览次数:40  
标签:vector vDis int 单源 C++ edges curDis INF 负权


负权图

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

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

负环图

存在负权边的单源最短路径的原理和C++实现_算法_02


 

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

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

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

原理

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

核心代码

template<const int INF=1000*1000*1000>
 class CBellMan
 {
 public:
     CBellMan(int n, const vector<vector<int>>& edges,int s , int k )
     {
         m_vDis.assign(n, INF);
         m_vDis[s] = 0;
         for (int i = 1; i <= k; i++)
         {
             vector<int> 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<int> m_vDis;
 };

测试样例

#include <vector>
 #include<assert.h>
 using namespace std; int main()
 {
     const int INF = 1000 * 1000 * 1000;
     vector<vector<int>> edges = { {0,1,1},{1,2,2},{2,3,3},{3,0,-7} };
     vector<vector<int>> 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<const int INF=1000*1000*1000>
 class CBellMan
 {
 public:
     CBellMan(int n, const vector<vector<int>>& edges,int s , int k )
     {
         m_vDis.assign(n, INF);
         m_vDis[s] = 0;
         for (int i = 1; i <= k; i++)
         {
             vector<int> curDis = m_vDis;
             Do(edges, curDis);
             m_vDis.swap(curDis);
         }
     }
     bool Check(const vector<vector<int>>& edges)
     {
         vector<int> 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<std::vector<int>>& edges, std::vector<int>& 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<int> m_vDis;
 };

测试样例

#include <vector>
 #include<assert.h>
 #include "BellMan.h"
 using namespace std; void Test1()
 {
     const int INF = 1000 * 1000 * 1000;
     vector<vector<int>> edges = { { 0,1,1 },{ 1,2,2 },{ 2,3,3 },{ 3,0,-7 } };
     vector<vector<int>> 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<vector<int>> edges = { { 0,1,1 },{ 1,2,2 },{ 2,3,3 },{ 3,0,-7 } };
     vector<int> 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();
 }


 

其它

测试环境

win7 VS2019 C++17

相关下载

源码及测试用例doc版文档,排版好

标签:vector,vDis,int,单源,C++,edges,curDis,INF,负权
From: https://blog.51cto.com/u_15724537/7934079

相关文章

  • 堆优化迪氏最短单源路径原理及C++实现
    时间复杂度O(ElogE),E是边数。适用与稀疏图。使用前提边的权为正。可以非连通,非连通的距离为-1。原理优选队列(小根堆)记录两个数据:当前点到源点距离,当前点。先处理距离小的点;如果距离相等,先处理谁都可以。可以用pair记录,不用重写小于。优先队列只记录如下情况的距离:一,{0,源点}。二,......
  • 有向图计数优化版原理及C++实现
    题目见前面章节。有向图访问计数的原理及C++实现第一版不需要拓扑排序,也不需要并集查找,直接dfs了。完成以下三个职责:一,DFS那些端点在环上。二,DFS环上各点此环的长度。三,DFS非环上各点。分析cur是当前dfs的节点,next为edges[cur]。从后向前分析:判定处理ret的值返回值找到环尾ret......
  • 朴素迪氏最短单源路径的原理及C++实现
    Dijkstra算法,翻译为迪杰斯特拉或狄克斯特拉。在下驽钝,记不住如此长的翻译,故简称迪氏。时间复杂度O(n2),端点数的平方。使用前提边的权为正。可以非连通,非连通的距离为-1。原理源点到源点的最短路径只有一个节点{s}。除源点本身外,其它端点的最短路径至少有两个端点,整个路径{s...x2}可......
  • 有向图访问计数的原理及C++实现
    题目现有一个有向图,其中包含n个节点,节点编号从0到n-1。此外,该图还包含了n条有向边。给你一个下标从0开始的数组edges,其中edges[i]表示存在一条从节点i到节点edges[i]的边。想象在图上发生以下过程:你从节点x开始,通过边访问其他节点,直到你在此过程中再次......
  • Windows下VC++编译器32位memcpy、memmove函数汇编代码详解
    整理者:赤勇玄心行天道QQ号:280604597微信号:qq280604597QQ群:511046632博客:www.cnblogs.com/gaoyaguo  blog.csdn.net/cyz7758520?type=blog大家有什么不明白的地方,或者想要详细了解的地方可以联系我,我会认真回复的!你可以随意转载,无需注明出处!写文档实属不易,我希望大家能支......
  • 深入实践C++11智能指针
    目录概念一、std::auto_ptr二、std::unique_ptr常用函数自定义智能指针对象持有的资源的释放函数三、std::shared_ptr常用函数四、std::enable_shared_from_this五、std::weak_ptr常用函数智能指针使用注意事项智能指针的简单实现概念C/C++语言最为人所诟病的特性之一就是......
  • C++常见入门题题解
    前言因为本人目前比较菜,所以给出的题解都是按照自己的学习进度来的,所以难度是一个循序渐进的过程,由于本人水平有限,望读者能够指出谬误,共同进步。回文数输出#include<bits/stdc++.h>//万能头usingnamespacestd;intmain(void){vector<int>font;//定义一个整型的向......
  • C++模板笔记
    参考文章:https://juejin.cn/post/7078530622527897631模板是C++的泛型编程机制,这种机制可以最大程度复用代码并且不会增加运行时开销模板分为函数模板和类模板函数模板函数模板是对函数的参数进行泛型化,传递给模板函数的类型实参可以是类,也可以是整型值,还可以是模板名比如://......
  • Qt/C/C++ 项目工程架构搭建设计经验
    摘要  独立负责一个Qt项目和经过前辈的教导后的一些架构搭建感悟,其中的各种理念对其他语言开发的项目也能有一定的互通,能带来更舒适的开发体验,谨代表个人开发的经验之谈,有需要的小伙伴酌情获取,辩证思考。也欢迎小伙伴们在评论区纠错补充。  关键词:C/C++、Qt、搭建框架、更......
  • 您需要了解的有关下一个MISRA®标准的信息:MISRA C++ 2023®简介
    我们在直播课中讲解过MISRAC通识及实践(请看VCR),今天我们来探讨一下MISRAC++:2023~ 【北汇信息|MISRAC通识及实践】 MISRAC++:2023®是广受期待的MISRAC++®标准的下一个版本,将于今年晚些时候发布。新版本将整合AUTOSARC++14指南,并支持C++的最新版本。 MISRA®是......