SPFA算法
(本文中的图片来源于博友的博客,图中有水印)
作用:找最短路径
说明:是dijkstra的优化,动态寻找各点的最短路径,尤其可以处理有向负权值
算法:
1.选取一个起点S,设定一个记录S到其它所有点距离的数组并初始化,操作如下:
①设定地图
②设定距离数组(S,S)=0,(S,Ki≠S)=inf
③设定处理队列{S}
2.对队列中的每一个点Ki进行以下操作:
①根据它所有的可行路径更新距离数组,更新过程中注意:
(1)如果目标点已经在队列中,不再入列,否则入列
(2)比较(S,Ki)+(Ki,Kj)和(S,Kj),记录较小的值
②弹出当前处理点Ki,检查队列中是否还有元素,如果有,处理下一个队首元素(步骤①),否则结束
3.距离数组中的值就是需要的S到各点的最短路程
操作:
如图,我们要找这个图的v1到其它点的最短路径
初始的数组记录如下:
处理队列:{v1}
从v1开始搜索可能的路径,更新如下:
此时,v1处理完毕,出列,而v3、v5、v6没有处理,入列(有点像BFS)
处理队列:{v3,v5,v6}
接下来处理v3,v3和上述点暂时还没有关联,继续更新:
处理队列:{v5,v6,v4}
然后处理v5,v5的处理涉及到v4和v6,当然也涉及到v1最短路径可能要修改
v5到v6的距离是60,而v4和v6在表中已经有值了,这时就要多一个操作,那就是比较(v1,v5)+(v5,vi)和表中已经存储的(v1,vi),将更短的哪一个存下来,于是更新如下:
上一步操作涉及到了v4和v6,它们已经在队列中,不需要重复入列
处理队列:{v6,v4}
再处理v6,没有可走路径,直接出列
处理队列:{v4}
v4到v6长度为10,比较10+50和90,发现(v1,v4,v6)是一个更好的选择,更新如下:
v6被更新了,所以v4出列,v6入列!
处理队列:{v6}
同样的,v6没有路可走,那么直接出列,队列空了,v1为源的遍历完成。其中v2不可达。
实战:
【题目一】
https://www.luogu.org/problemnew/show/P2683
#include<iostream>//long long不能做数组下标
#include<cstdio>
#include<cstring>
using namespace std;
int n,m;
int u,v,d;
int s,e;//起点 终点
int pd;
int exist[1000];//记录是否入队
int team[10000001];//队列
int dis[1001];//最短路
int g[101][101];//邻接矩阵存图
int head,tail;
int spfa()
{
memset(dis,0x7f,sizeof(dis));
dis[s]=0;
memset(exist,false,sizeof(exist));
team[1]=s;
head=0;
tail=1;
exist[s]=true;//以上为spfa初始化
do
{
++head;
u=team[head];
exist[u]=false;//取出一个点
for(v=1;v<=n;++v)
{
if(dis[v]>dis[u]+g[u][v])
{
dis[v]=dis[u]+g[u][v];//松弛操作,更新
if(!exist[v])
{
++tail;
team[tail]=v;
exist[v]=true;//如果被更新的点没在队列里就入队
}
}
}
}while(head<tail);
}
int main()
{
cin>>n>>m;
memset(g,0x7f,sizeof(g));//邻接矩阵初始化
for(int i=1;i<=m;++i)
{
cin>>pd;
if(pd==1)
{
cin>>u>>v>>d;
if(d<g[u][v])
{
g[u][v]=g[v][u]=d;//更新边的信息
}
}
else
{
cin>>s>>e;
spfa();//求s到e的最短路
if(dis[e]>10000000) cout<<-1<<endl;//如果s到e的距离太大则不存在路
else cout<<dis[e]<<endl;
}
}
return 0;
}