算法之SPFA的前置:Bellman-Ford算法
SPFA
我们都知道一个叫SPFA的算法,它是用来计算单源最短路径的,但是,众所周知它不是很稳定,容易退化。
SPFA是基于什么被提出的?
基于一个叫做Bellman-Ford的算法。
Bellman-Ford
bellman算法实际上比dijkstra有更高的普适性,因为它可以处理有负边权图。但无法处理存在负权回路情况。
算法的时间复杂度为\(O(VE)\),V是顶点数,E是边数。
以下是bellman-ford的伪代码:
#define MAXN 最大点数
#define MAXM 最大边数
int dis[MAXN],w[MAXM],s;//dis 表示从出发点s到i点的当前最短路径长度,w表示边权值,s表示出发点。
struct lines{//边集结构体
int u,v;//出发点,到达点
}l[MAXM];
/*
初始化,将dis[s]设为0,dis[其他]设为0x3f3f3f3f,输入边集。
*/
for(int i=1;i<=MAXN;i++){
for(int j=1;j<=MAXM;j++){
if(dis[l[j].u]+w[j]<dis[l[j].v]){
dis[l[j].v]=dis[l[j].u]+w[j];//核心代码
}
}
}
//算法结束,dis[i]就是s点到i点的最短路,若dis[i]==0x3f3f3f3f则从s点无法到达该点。
ford算法很好理解,容易看出是一个一维Dp(实际上还有二维dp写法,蒟蒻不会……)。
以下是几个常见不理解的问题:
为什么distance初始化除起点外设成正无穷?
这个……肯定是为了接下来的更新啊!
对于无向图和有向图,这个算法需要变化吗?
说到点上了!对于有向图一定要记住,伪代码中的u,一定是边的入节点!相应的v,一定是出节点!
如果是无向图,我们需要做的就是反过来再执行一次。
也就是原来是这个:
if(dis[l[j].u]+w[j]<dis[l[j].v])dis[l[j].v]=dis[l[j].u]+w[j];
变成了这个:
if(dis[l[j].u]+w[j]<dis[l[j].v])dis[l[j].v]=dis[l[j].u]+w[j];
if(dis[l[j].v]+w[j]<dis[l[j].u])dis[l[j].u]=dis[l[j].v]+w[j];
到底什么是所谓的松弛操作?
松弛操作,很简单,就是这句代码:
if(dis[l[j].u]+w[j]<dis[l[j].v])dis[l[j].v]=dis[l[j].u]+w[j];
惊不惊喜?
原来听了这么久松弛,还以为是啥奇怪的东西,没想到已经用上了!
为什么外层循环要用n次?
这个嘛……实际上仔细想想可以发现,运用蓝白点的思想,一开始起点S是白点,其他都是蓝点。然后由于如果还有蓝点,那么一定还有边连接着蓝点和白点。每次外层循环一定可以遍历到一些这样的边,也一定至少有一个蓝点变成白点。
这样的话,我们执行n遍外层循环,一定能保证所有点都求出了最终结果。
为什么说它不适用于负权回路?
负权回路是什么?
负权回路是指,图上一个回路,各边权值之和是负数。
相信看到这里你已经发现,如果图上存在负权回路,那么至少有两点间的最短路会是无限小!
因为可以绕这个回路走无限圈,最短路也跟着无限小……
所以:存在负权回路的图无法求出最短路。
注意是无法!目前已知算法都无法!
bellman-ford算法可以在算法结束时给出错误提示,以判断这张图是否存在负权回路:
方法:在核心代码全部结束后,执行以下代码,若仍存在某条边,使得dis[l[j].u]+w[j]<dis[l[j].v]
那么我们可以判定该图存在负权回路:
for(int i=1;i<=MAXM;i++){
if(dis[l[j].u]+w[j]<dis[l[j].v])//错误提示。
}
挣扎着优化 ∑( 口 ||
对bellman算法,我们还可以有一个优化,我们看到,bellman算法有的时候会在外层循环没结束的时候就完成了所有松弛操作!
所以我们可以加一个计数:记下每次大循环中松弛的次数,当某次循环不进行松弛时,我们认定算法结束,直接跳出大循环。
#define MAXN 最大点数
#define MAXM 最大边数
int dis[MAXN],w[MAXM],s;
struct lines{
int u,v;
}l[MAXM];
int tot;
for(int i=1;i<=MAXN;i++){
tot=0;//变动
for(int j=1;j<=MAXM;j++){
if(dis[l[j].u]+w[j]<dis[l[j].v]){
tot++;//变动
dis[l[j].v]=dis[l[j].u]+w[j];
}
}
if(!tot)break;//变动
}
而这个小小优化,有什么作用呢?
70pts -> 100pts
AC记录
一个小操作,可以顺利通过这道题(虽然是弱化版)
劣势
bellman-ford算法的缺点其实刚刚已经体现出来了!
就是,外层循环有大量重复计算!
蒟蒻过几天会写一个SPFA的讲解,SPFA其实就是ford的队列优化。
完结撒花
算法之SPFA的前置:Bellman-Ford算法的更多相关文章
- Bellman—Ford算法思想
---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...
- Bellman - Ford 算法解决最短路径问题
Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力 ...
- Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】
题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...
- poj1860 bellman—ford队列优化 Currency Exchange
Currency Exchange Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 22123 Accepted: 799 ...
- uva 558 - Wormholes(Bellman Ford判断负环)
题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...
- 数据结构与算法--最短路径之Bellman算法、SPFA算法
数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...
- 最短路径——Bellman-Ford算法以及SPFA算法
说完dijkstra算法,有提到过朴素dij算法无法处理负权边的情况,这里就需要用到Bellman-Ford算法,抛弃贪心的想法,牺牲时间的基础上,换取负权有向图的处理正确. 单源最短路径 Bellm ...
- 图论之最短路算法之SPFA算法
SPFA(Shortest Path Faster Algorithm)算法,是一种求最短路的算法. SPFA的思路及写法和BFS有相同的地方,我就举一道例题(洛谷--P3371 [模板]单源最短路径 ...
- HDOJ 2544 最短路(最短路径 dijkstra算法,SPFA邻接表实现,floyd算法)
最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...
- Bellman-ford算法、SPFA算法求解最短路模板
Bellman-ford 算法适用于含有负权边的最短路求解,复杂度是O( VE ),其原理是依次对每条边进行松弛操作,重复这个操作E-1次后则一定得到最短路,如果还能继续松弛,则有负环.这是因为最长的 ...
随机推荐
- 驱动开发:内核测试模式过DSE签名
微软在x64系统中推出了DSE保护机制,DSE全称(Driver Signature Enforcement),该保护机制的核心就是任何驱动程序或者是第三方驱动如果想要在正常模式下被加载则必须要经过微 ...
- 大文件分片上传,后端拼接保存(前端:antd;后端:.Net 5 WebAPI)
前言 对于普通业务场景而言,直接用 FormData() 将文件以入参的一个参数传给后端即可,但此方法有一个弊端就是,有个 30M 的上限. 对于动辄几百 M.几个 G 的文件上传需求,FormDat ...
- C# RulesEngine 规则引擎:从入门到看懵
说明 RulesEngine 是 C# 写的一个规则引擎类库,读者可以从这些地方了解它: 仓库地址: https://github.com/microsoft/RulesEngine 使用方法: ht ...
- 现代 CSS 指南 -- at-rule 规则扫盲
大部分同学都用过 CSS 的屏幕宽度媒体查询,像是这样: @media screen and (min-width: 900px) { div { padding: 1rem 3rem; } } 这里 ...
- 对比entrypoint以及cmd
如何查看 现有 images 的 入口信息 docker image inspect image_id { "entrypoint": "xxx" " ...
- C++实现真值表
这一片文章主要是关于真值表,在完成之前我也遇到了许多问题.比如怎么去求解表达式的值,怎么去将每个变量进行赋值,也就是如何 将n个字符进行01全排列. 01全排列真的神奇,01全排列其实就是2^n.他可 ...
- KafkaOffsetMonitor:监控消费者和延迟的队列
个人名片: 因为云计算成为了监控工程师 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 目录 消费者组列表 消费组的topic列表 图中参数含义解释如下: topic的历史位置 O ...
- 为什么你的static_assert不能按预期的工作?
static_assert是c++11添加的新语法,它可以使我们在编译期间检测一些断言条件是否为真,如果不满足条件将会产生一条编译错误信息. 使用静态断言可以提前暴露许多问题到编译阶段,极大的方便了我 ...
- ubuntu 22.04安装多个gcc
sudo apt install gcc-9 g++-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 - ...
- 快速创建Jenkins Job
Jenkins Job 类型 **1.Freestyle project ** 这个是jenkins的基础功能,可以用它来执行各种构建任务,他只能构建在一个电脑上,如果没有太多的需求,这个job基本够 ...