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算法的更多相关文章

  1. Bellman—Ford算法思想

    ---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...

  2. Bellman - Ford 算法解决最短路径问题

    Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力 ...

  3. Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】

    题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...

  4. poj1860 bellman—ford队列优化 Currency Exchange

    Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22123   Accepted: 799 ...

  5. uva 558 - Wormholes(Bellman Ford判断负环)

    题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...

  6. 数据结构与算法--最短路径之Bellman算法、SPFA算法

    数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...

  7. 最短路径——Bellman-Ford算法以及SPFA算法

    说完dijkstra算法,有提到过朴素dij算法无法处理负权边的情况,这里就需要用到Bellman-Ford算法,抛弃贪心的想法,牺牲时间的基础上,换取负权有向图的处理正确. 单源最短路径 Bellm ...

  8. 图论之最短路算法之SPFA算法

    SPFA(Shortest Path Faster Algorithm)算法,是一种求最短路的算法. SPFA的思路及写法和BFS有相同的地方,我就举一道例题(洛谷--P3371 [模板]单源最短路径 ...

  9. HDOJ 2544 最短路(最短路径 dijkstra算法,SPFA邻接表实现,floyd算法)

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  10. Bellman-ford算法、SPFA算法求解最短路模板

    Bellman-ford 算法适用于含有负权边的最短路求解,复杂度是O( VE ),其原理是依次对每条边进行松弛操作,重复这个操作E-1次后则一定得到最短路,如果还能继续松弛,则有负环.这是因为最长的 ...

随机推荐

  1. "一键重装系统软件"操作流程

    博主之前重装系统都是直接用win10系统更新里的重置系统(版本不会变化),和U盘重装电脑出厂映像(有各种品牌自带的软件,而且由于是出场版本,版本很低) 所以这次用"小白一键重装系统" ...

  2. 基于JESD204B和PCIe DMA的多通道数据采集和回放系统

    基于JESD204B和PCIe DMA的多通道数据采集和回放系统 在主机端PCIe驱动的控制和调度下,数据采集与回放系统可以同时完成对多个JESD204B接口AD数据的采集以及JESD204B接口DA ...

  3. Vivado_8位流水灯

    Design 代码中的计数器设置是因为我的开发板的时钟是100MHZ的,也就是1秒完成了100_000_000次时钟信号,所以我设置计数器为100_000_000次. 也就是说,我让流水灯的变化周期为 ...

  4. 树上启发式合并(dsu on tree)

    树上启发式合并属于暴力的优化,复杂度O(nlogn) 主要解决的问题特点在于: 1.对于树上的某些信息进行查询 2.一般问题的解决不包含对树的修改,所有答案可以离线解决 算法思路:这类问题的特点在于父 ...

  5. 修改Oracle共享池大小

    1.  sysdba登录数据库 [oracle@ufdb165 ~]$ sqlplus /nolog SQL*Plus: Release 11.2.0.4.0 Production on Wed Au ...

  6. IO学习笔记

    IO File 概述 构造方法 代码实现: public class FileDemo001 { public static void main(String[] args) { File f1 = ...

  7. C#程序自启动

    在窗体加载事件里面加入下述代码: //设置开机自启动 RegistryKey registryKey = Registry.CurrentUser.OpenSubKey ("SOFTWARE ...

  8. 解决"raise EnvironmentError("%s not found" % (_mysql_config_path,)) OSError: mysql_config not found"报错

    redhat系(如centos) yum install mysql-devel debain系(如ubuntu) sudo apt-get install libmysqlclient-dev

  9. vue阻止向上和向下冒泡

    阻止向下冒泡 <div class="content" @click.self="cancelFunc"></div> 阻止向上冒泡 & ...

  10. day25 前端

    https://www.dcloud.io/hbuilderx.html 下载HbuilderX,直接解压缩双击打开 html5 <!DOCTYPE html><!-- 文档类型,声 ...