SPFA算法:
主要是为了求解图中的单源最短路径(可以含负权边),是对Bellman-Ford 的优化,但无法完全取代Bellman-Ford。SPFA算法可以解决,Dijkstra算法无法解决的权值为负的情况。
为什么出现负权值dijkstra算法失效
我们看张图,从1号结点开始执行dijkstra算法,我们在权值6和5的边中选择权值为5的边此时来到二号结点d[2]=5,然后选择边权为-2的边更新d[3]=3,结束dijkstra算法。我们可以看到结束时d[2]=5,而实际上d[2]应该=4才是最短路径,也就是从1–>3–>2这样走才是最短的。这是因为dijkstra算法实际上是贪心算法,所以导致了错误的发生!
SPFA算法实现过程
我们用一个队列,每次拿去队首元素,先取消标记表示已经出队,然后遍历与队首元素相关的边,更新他们的d[v],如果结点不在队中,那么将该结点入队,并标记!依此类推,每次拿出队首,取消标记,更新d[v],若不在队中就标记入队,直到队列为空我们就可以得出起始点到所有点的最短路径。
如何判断是否更新d[v]?
我们用当前结点u的d[u]+此边边权判断是否小于原来的d[v],如果小于更新d[v],在判断是否在队中,如果在不入队,不在则入队!
顺序问题
先判断是否更新d[v]–>如果更新d[v]–>判断是否在队列–>不在入队!
(不管v点在不在队列,只要d[v]>d[u]+此边边权就更新)
实现代码:
void spfa(int start)
{
d[start]=0;
vis[start]=1;//标记入队
queue<int> q;
q.push(start);
while(!q.empty())
{
int t=q.front();
q.pop();
vis[t]=0;//出队取消标记
//遍历与u点相邻结点
for(int i=0;i<M[t].size();i++)
{
edge e=M[t][i];
if(d[e.to]>d[t]+e.v)
{
d[e.to]=d[t]+e.v;//更新d[v]
if(!vis[e.to])
{
vis[e.to]=1;//入队标记
q.push(e.to);
}
}
}
}
}
复杂度分析
SPFA的复杂度最好是O(kE),最坏是O(VE),k值取决于入队个数,因为有的点要入队两次或者更多次,通常k=2左右!最坏情况就是O(VE)。
spfa可以解决点在1e5以下的题目!
一道例题
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=220;
struct point{//坐标
int x,y;
}P[maxn];
struct edge{//边
int to;
double v;
edge(int to,double v):to(to),v(v){}//初始化
};
vector<edge> M[maxn];//存放边的邻接表
int vis[maxn];//d存储到第i号结点的最短记录,vis记录是否拜访过
double d[maxn];
//dis算距离
double dis(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int n,x,y,m,u,v;
void spfa(int start)
{
d[start]=0;
vis[start]=1;//标记入队
queue<int> q;
q.push(start);
while(!q.empty())
{
int t=q.front();
q.pop();
vis[t]=0;//出队取消标记
//遍历与u点相邻结点
for(int i=0;i<M[t].size();i++)
{
edge e=M[t][i];
if(d[e.to]>d[t]+e.v)
{
d[e.to]=d[t]+e.v;//更新d[v]
if(!vis[e.to])
{
vis[e.to]=1;//入队标记
q.push(e.to);
}
}
}
}
}
int main()
{
scanf("%d",&n);
//读入前n家店的坐标
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
P[i].x=x;
P[i].y=y;
}
scanf("%d",&m);
//读入边的信息
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
M[u].push_back(edge(v,dis(P[u],P[v])));
M[v].push_back(edge(u,dis(P[u],P[v])));
}
scanf("%d%d",&u,&v);
spfa(u);
printf("%.2f",d[v]);
return 0;
}
感谢观看,大家多多点赞支持!