SPFA详解
来源:互联网 发布:php 上传txt 编辑:程序博客网 时间:2024/05/29 11:20
引子
定义
SPFA是Shortest Path Faster Algorithm,是Bellman-Ford算法的改进版。和其他最短路算法一样,都是以松弛操作的三角形不等式为基础操作的。
优点
SPFA算法用途广,适应负权,还能判断正环和负环……在差分约束建模中也有重大用处……SPFA是个好东西
SPFA的实现
spfa有两种实现方式,一种是栈实现,一种是队列实现。
在有负环的情况下,栈比队列更快,但是如果没有负环的一般情况下,队列更快。
栈实现
用栈实现的spfa实际上跟dfs很像,实际上也可以说就是dfs。采用邻接表可以很好的实现,时间复杂度
队列实现
先把源点进队,然后用源点扩展更新,能迭代的都进队……
就是用队列里的点去迭代其他点,被迭代的点再次进队。
时间复杂度
SPFA的应用
spfa在noip中通常用于求最短路/判负环。求最短路,spfa相比Dijkstra、Bellman-Ford等算法有着优秀的时间复杂度和剪枝,还有自己的特色:可以判负环。因为有可以判负环的性质,一般还用在差分约束系统建模后的一些判断工作。
求最短路
单源最短路径模板题
这道题目n数据量10000,m数据量500000,明显floyd超时超空间,Dijkstra也超时。
所以我们用了spfa:
#include<bits/stdc++.h>#define maxn 100010#define maxm 500010#define maxint 2147483647using namespace std;inline int read(){ int num=0; char c=getchar(); for(;c<'0'||c>'9';c=getchar()); for(;c>='0'&&c<='9';c=getchar())num=num*10+c-'0'; return num;}int n,m,s,top,dis[maxn*4];bool vis[maxn*4];struct node{ int nxt,len,order; }a[maxm*4];int ljhead[maxn*4];int q[maxn*3],head,tail=0;void addedge(int x,int y,int length){ top++; a[top].len=length; a[top].nxt=ljhead[x]; a[top].order=y; ljhead[x]=top;}//邻接表插入边。void init(){ n=read(); m=read(); s=read(); for(int i=1;i<=m;i++) { int x,y,length; x=read(); y=read(); length=read(); addedge(x,y,length); }}void SPFA(){ for(int i=1;i<=n;i++) { dis[i]=maxint; vis[i]=false; } dis[s]=0; vis[s]=true; int v; head=0;tail=1;q[1]=s; while(tail-head+1>0) { v=q[++head]; for(int i=ljhead[v];i;i=a[i].nxt) if(dis[v]+a[i].len<dis[a[i].order]) { dis[a[i].order]=dis[v]+a[i].len; if(vis[a[i].order]==false) { vis[a[i].order]=true; tail++; q[tail]=a[i].order; } } vis[q[head]]=false; }}int main(){ init(); SPFA(); for(int i=1;i<=n;i++) printf("%d ",dis[i]); return 0;}
判断负环
负环模板题
这道题目是板子题啊,我们直接上dfs_spfa!
#include<bits/stdc++.h>#define maxn 200200#define INF 2e9using namespace std;inline int read(){ int num=0; bool flag=true; char c; for(;c>'9'||c<'0';c=getchar()) if(c=='-') flag=false; for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar()); return flag ? num : -num;}struct node{ int to,val;};int dis[maxn],t,n,m;bool vis[maxn],flag;vector<node>G[maxn];void insert(int x,int y,int w){ G[x].push_back((node){y,w});}void init(){ n=read(); m=read(); for(int i=0;i<=n;i++) { dis[i]=INF; vis[i]=false; G[i].clear(); } for(int i=1;i<=m;i++) { int x=read(); int y=read(); int w=read(); if(w<0) { insert(x,y,w); } else { insert(x,y,w); insert(y,x,w); } }}void spfa(int u){ vis[u]=true; for(int i=0;i<G[u].size();i++) { int to=G[u][i].to;//是这条边对面的点 if(dis[u]+G[u][i].val<dis[to]) { dis[to]=dis[u]+G[u][i].val; if(vis[to]) { flag=true; return ; } else { spfa(to); } } } vis[u]=false;}int main(){ t=read(); while(t--) { init(); flag=false; dis[1]=0; spfa(1); if(flag) { printf("YE5\n"); } else { printf("N0\n"); } } return 0; }
但是可惜啊!tle3个点,O-2优化开了也没用!
我们需要思考下dfs_spfa的优化了!
判断负环的优化
既然我们只需要判断负环,那么就相当于我们需要找到一条权值和为负的回路。
既然我们只需要找到权值和为负的回路,那不妨使距离数组dis初始化为0。
这样处理后,第一次拓展只会拓展到与起点相连边权为负的边。
那么我们就分别枚举所有的点作为起点,如果已经找到一个负环就不再继续枚举。
根据SPFA,我们找到的负环一定包含当前枚举的这个点。
算法期望复杂度
#include<bits/stdc++.h>#define maxn 200200#define INF 2e9using namespace std;inline int read(){ int num=0; bool flag=true; char c; for(;c>'9'||c<'0';c=getchar()) if(c=='-') flag=false; for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar()); return flag ? num : -num;}struct node{ int to,val;};int dis[maxn],t,n,m;bool vis[maxn],flag;vector<node>G[maxn];void insert(int x,int y,int w){ G[x].push_back((node){y,w});}void init(){ n=read(); m=read(); for(int i=0;i<=n;i++) { dis[i]=0;//这里由于要枚举点,所以要赋值为0; vis[i]=false; G[i].clear(); } for(int i=1;i<=m;i++) { int x=read(); int y=read(); int w=read(); if(w<0) { insert(x,y,w); } else { insert(x,y,w); insert(y,x,w); } }}void spfa(int u)//spfa基本上没有什么变化{ if(flag)return; vis[u]=true; for(int i=0;i<G[u].size();i++) { if(flag)return; int to=G[u][i].to; if(dis[u]+G[u][i].val<dis[to]) { dis[to]=dis[u]+G[u][i].val; if(vis[to]) { flag=true; return ; } else { spfa(to); } } } vis[u]=false;}int main(){ t=read(); while(t--) { init(); flag=false; dis[1]=0; for(int i=1;i<=n;i++) { spfa(i);//枚举每一个点 if(flag)break; } if(flag) { printf("YE5\n"); } else { printf("N0\n"); } } return 0; }
原来的程序运行时间是5112ms,优化后的程序运行时间312ms……
差分约束系统
我们在差分约束题目中会得出一系列代数约束关系,例如
详见:差分约束详解by柴犬首相
高阶版敬请期待
GG
- SPFA详解
- SPFA详解
- SPFA算法详解
- SPFA算法详解
- SPFA算法详解
- spfa算法详解
- SPFA基础详解
- SPFA 算法详解
- SPFA 算法详解
- SPFA算法详解
- SPFA单源最短路详解
- spfa 算法详解
- SPFA 算法详解
- SPFA 算法详解
- SPFA算法详解
- SPFA 算法详解
- (转)SPFA算法详解
- 最短路SPFA 算法详解
- Keep On Movin HDU
- jquery.page创建分页
- jquery设置My97DatePicker日期选择插件只能以今天为起点。
- Linux集群配置SSH互信
- textAngular 文本编辑器的用法
- SPFA详解
- oracle操作数据库命令
- 九、java多态的理解
- 进程与线程
- 悉心指点下写出的 加1乘2平方 虽然估计没多少人会看
- 关于For语句
- Java三大器之监听器(Listener)的工作原理和代码演示
- JS-三种对话框
- tensorflow系列笔记:流程,概念和代码解析