SPFA 算法(剪辑)(学习!)
SPFA算法
单源最短路径的算法最常用的是Dijkstra,些算法从时间复杂度来说为O(n^2),但是面对含有负权植的图来说就无能为力了,此时 Dellman-ford算法就有用了,这咱算法是采用的是动态规化的思想,但是1994年西南交通大学段凡丁发表了SPFA(Shortest Path Faster Algorithm)听这个名字就懂了,这种算法在时间上一定很快了。它是对Dellman-ford的优化,所以建议今后直接学SPFA。很多时候,给 定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。
思路:
简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
和上文一样,我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且 v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
定理3 只要最短路径存在,上述SPFA算法必定能求出最小值。
证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于 我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最 短路径估计值就是对应结点的最短路径值。(证毕)
刚才我们只是笼统地说SPFA算法在效率上有过人之处,那么到底它的复杂度是怎样的?
定理4 在平均情况下,SPFA算法的期望时间复杂度为O(E)。
证明:上述算法每次取出队首结点u,并访问u的所有临结点的复杂度为O(d),其中d为点u的出度。运用均摊分析的思想,对于|V|个点|E|条边的图,点的平均出度为,所以每处理一个点的复杂度为O( )。假设结点入队的次数h,显然h随图的不同而不同。但它仅与边的权值分布有关。我们设 h=kV,则算法SPFA的时间复杂度为。在平均的情况下,可以将k看成一个比较小的常数,所以SPFA算法在一般情况下的时间复杂度为O(E)。(证毕)
聪明的读者一定发现了,SPFA和经过简单优化的Bellman-Ford无论在思想上还是在复杂度上都有相似之处。确实如此。两者的思想都属于标号修正 的范畴。算法是迭代式的,最短路径的估计值都是临时的。算法思想是不断地逼近最优解,只在最后一步才确定想要的结果。但是他们实现的方式上存在差异。正因
为如此,它们的时间复杂度其实有较大差异的。在Bellman-Ford算法中,要是某个点的最短路径估计值更新了,那么我们必须对所有边指向的终点再做一次松弛操作;在SPFA算法中,某个点的最短路径估计值更新,只有以该点为起点的边指向的终点需要再做一次松弛操作。在极端情况下,后者的效率将是前者的n倍,一般情况下,后者的效率也比前者高出不少。基于两者在思想上的相似,可以这样说,SPFA算法其实是Bellman-Ford算法的一个进一步优化的版本。
算法流程
算法大致流程是用一个队列来进行维护。初始时将源加入队列。每次从队列中取出一个元素,并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。直到队列为空时算法结束。
这个算法,简单的说就是队列优化的bellman-ford,利用了每个点不会更新次数太多的特点发明的此算法
SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。SPFA的实现甚至比Dijkstra或者Bellman_Ford还要简单:
设Dist代表S到I点的当前最短距离,Fa代表S到I的当前最短路径中I点之前的一个点的编号。开始时Dist全部为+∞,只有Dist[S]=0,Fa全部为0。
维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点S。用一个布尔数组记录每个点是否处在队列中。
每次迭代,取出队头的点v,依次枚举从v出发的边v->u,设边的长度为len,判断Dist[v]+len是否小于 Dist[u],若小于则改进Dist[u],将Fa[u]记为v,并且由于S到u的最短距离变小了,有可能u可以改进其它的点,所以若u不在队列中,就将它放入队尾。这样一直迭代下去直到队列变空,也就是S到所有的最短距离都确定下来,结束算法。若一个点入队次数超过n,则有负权环。
SPFA 在形式上和宽度优先搜索非常类似,不同的是宽度优先搜索中一个点出了队列就不可能重新进入队列,但是SPFA中一个点可能在出队列之后再次被放入队列,也 就是一个点改进过其它的点之后,过了一段时间可能本身被改进,于是再次用来改进其它的点,这样反复迭代下去。设一个点用来作为迭代点对其它点进行改进的平
均次数为k,有办法证明对于通常的情况,k在2左右。
代码:
存图的方式我是利用链式向前星的方式存图的,下面是代码:
#include
#include
#include
#define Maxn 100
#define Maxm 10000
#define Max 10000
int used[Maxn],outqueue[Maxn],head[Maxn],queue[Maxn],low[Maxn],n,m;
struct Edge
{
int to,w,next;
}edge[Maxm];
bool SPFA(int start)
{
int i =0, iq = 0;
used[start] = 1;
queue[iq++] = start;
low[start] = 0;
while (i != iq)
{
int top =
queue[i];
used[top] = 0;
outqueue[top]++;//用来判断是否有环路
if (outqueue[top]
> n) return false;
for (int k =
head[top]; k != -1; k = edge[k].next)//宽搜每条边
{
if (low[edge[k].to] > low[top] + edge[k].w)//对点进行松驰
low[edge[k].to] = low[top] + edge[k].w;
if (!used[edge[k].to])
{
used[edge[k].to] = 1;
queue[iq++] = edge[k].to;
}
}
i++;
}
return true;
}
int main()
{
while (scanf ("%d%d", &n ,&m) != EOF)
{
memset (used, 0 ,sizeof(used));
memset (head, -1
,sizeof(head));
memset (outqueue, 0
,sizeof(outqueue));
memset (low, Max,
sizeof(low));
int k = 0;
while (m--)
{
int a,b,w;
scanf ("%d%d%d", &a, &b, &w);
edge[k].to = b;
edge[k].w = w;
edge[k].next = head[a];
head[a] = k++;
}
if (SPFA(1))
printf
("%d\n", low[n]);
else
printf
("不存在最短\n");
}
}
刚才那个代码是最短路里面效率最高的,为了提高效率,队列那个是自己写的,而不是调用stl里面的,但是看起来比较难,很难读懂,下面是用stl里面的queue写的。
完整代码:
// SPFA算法 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <queue>
#include <algorithm> #define Maxn 100
#define Maxm 10000
#define Max 10000 using namespace std; int used[Maxn],outqueue[Maxn];
int head[Maxn],low[Maxn];
int n,m; struct Edge
{
int to, w, next ;
}edge[Maxm]; bool SPFA (int start)
{
queue<int>a ;
used[start] = 1;
low[start] = 0;
a.push(start);
while (!a.empty())
{
int top = a.front();
a.pop();
outqueue[top]++;
if (outqueue[top] > n) return false;
for (int k = head[top]; k!= -1; k = edge[k].next)
{
if (low[edge[k].to] > low[top] + edge[k].w)
low[edge[k].to] = low[top] + edge[k].w;
if (!used[edge[k].to])
{
used[edge[k].to] = 1;
a.push(edge[k].to);
}
}
}
return true;
}
int main()
{
while (scanf ("%d %d", &n ,&m) != EOF)
{
memset (used, 0 ,sizeof(used));
memset (head, -1 ,sizeof(head));
memset (outqueue, 0 ,sizeof(outqueue));
memset (low, Max, sizeof(low));
int k = 0;
while (m--)
{
int a,b,w;
scanf ("%d %d %d", &a, &b, &w);
edge[k].to = b;
edge[k].w = w;
edge[k].next = head[a];
head[a] = k++;
}
if (SPFA(1))
printf ("%d\n", low[n]);
else
printf ("不存在最短\n");
}
return 0;
}
SPFA 算法(剪辑)(学习!)的更多相关文章
- SPFA算法学习笔记
一.理论准备 为了学习网络流,先水一道spfa. SPFA算法是1994年西南交通大学段凡丁提出,只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在 ...
- 转载:SPFA算法学习
转载地址:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html 粗略讲讲SPFA算法的原理,SPFA算法是1994年西 ...
- 用scheme语言实现SPFA算法(单源最短路)
最近自己陷入了很长时间的学习和思考之中,突然发现好久没有更新博文了,于是便想更新一篇. 这篇文章是我之前程序设计语言课作业中一段代码,用scheme语言实现单源最段路算法.当时的我,花了一整天时间,学 ...
- 2018/1/28 每日一学 单源最短路的SPFA算法以及其他三大最短路算法比较总结
刚刚AC的pj普及组第四题就是一种单源最短路. 我们知道当一个图存在负权边时像Dijkstra等算法便无法实现: 而Bellman-Ford算法的复杂度又过高O(V*E),SPFA算法便派上用场了. ...
- hihocoder 1093 SPFA算法
题目链接:http://hihocoder.com/problemset/problem/1093 , 最短路的SPFA算法. 由于点的限制(10w),只能用邻接表.今天也学了一种邻接表的写法,感觉挺 ...
- 关于SPFA算法的优化方式
关于SPFA算法的优化方式 这篇随笔讲解信息学奥林匹克竞赛中图论部分的求最短路算法SPFA的两种优化方式.学习这两种优化算法需要有SPFA朴素算法的学习经验.在本随笔中SPFA朴素算法的相关知识将不予 ...
- Bellman-ford算法、SPFA算法求解最短路模板
Bellman-ford 算法适用于含有负权边的最短路求解,复杂度是O( VE ),其原理是依次对每条边进行松弛操作,重复这个操作E-1次后则一定得到最短路,如果还能继续松弛,则有负环.这是因为最长的 ...
- POJ 3013 SPFA算法,邻接表的使用
Big Christmas Tree Time Limit: 3000MS Memory Limit: 131072K Total Submissions: 19029 Accepted: 4 ...
- 【算法】单元最短路径之Bellman-Ford算法和SPFA算法
SPFA是经过对列优化的bellman-Ford算法,因此,在学习SPFA算法之前,先学习下bellman-Ford算法. bellman-Ford算法是一种通过松弛操作计算最短路的算法. 适用条件 ...
随机推荐
- 关于Linux网络配置
Linux网络配置 一:什么是网络接口卡以及如何查看网络接口的网络信息:在Linux系统中,主机的网络接口卡通常称为“网络接口”,我们可以使用ifconfig命令来查看网络 接口的信息(普通用户使用/ ...
- 定时执行线程池ScheduledExecutorService的使用
ScheduledExecutorService progressExecutorService = Executors.newScheduledThreadPool(1); ScheduledFut ...
- IOS开发——Core Graphics & Core Animation
好久没写过blog了.首先了解下近期苹果和IOS方面的最新消息. 1.WWDC2014在上个月举行了,与2013年一样.今年WWDC没公布硬件产品和新品(假设你懂cook你就会期待今年秋季公布会.估计 ...
- Atitit.go语言golang语言的新的特性 attilax总结
Atitit.go语言golang语言的新的特性 attilax总结 1. 继承树less 动态接口1 1.1. 按照书中说的,Go语言具有以下的特征,下面我们分别来进行介绍. q 自动垃圾回收 ...
- APICloud打包Vue单页应用
APICloud新建项目后,会生成以下目录结构 其中index.html是入口文件,而vue-cli打包生成的文件是在dist目录下 ├─dist│ └─static│ ├─css│ └─js │ └ ...
- 第二篇:Filebeat 安装配置
Filebeat 简介:Filebeat 是一款轻量型日志收集工具,可转发汇总日志.文件等内容. 其主要特点为:1. 断点续传.(如遇日志转发过程中网络 ...
- Downloading jQuery
Compressed and uncompressed copies of jQuery files are available. The uncompressed file is best used ...
- 谷歌浏览器console.log()失效,打印不出来内容
这个问题困扰好几天了,网上说的都说的是下图: 勾选这三个就好了,但是我的本来就是勾选上的,还是不行. 后来发现这个: 把这个去掉就可以了,如下图: 原来是因为之前调试js的时候,使用了这个过滤,导致对 ...
- Android 适配(drawable文件夹)图片适配(二)
参考自(https://blog.csdn.net/myoungmeng/article/details/54090891) Android资源文件存放: android的drawable文件一共可以 ...
- 【BZOJ2780】[Spoj]8093 Sevenk Love Oimaster 广义后缀自动机
[BZOJ2780][Spoj]8093 Sevenk Love Oimaster Description Oimaster and sevenk love each other. But r ...