一.算法简介
SPFA(Shortest Path Faster Algorithm)是一种用于解决单源最短路径问题的算法,它是对Bellman-Ford算法的一种优化。SPFA算法通过不断地更新节点的最短路径估计值来逐步逼近最短路径。
下面是SPFA算法的基本步骤:
- 初始化:将起始节点的最短路径估计值设为0,其他节点的最短路径估计值设为无穷大。
- 将起始节点加入一个队列中。
- 从队列中取出一个节点,遍历该节点的所有邻接节点。
- 对于每个邻接节点,如果通过当前节点可以获得更短的路径,则更新该节点的最短路径估计值,并将该节点加入队列中。
- 重复步骤3和步骤4,直到队列为空。
- 最终得到每个节点的最短路径估计值。
SPFA算法的优化在于使用了队列来存储待处理的节点,而不是像Bellman-Ford算法那样使用了一个集合。这样可以避免重复处理节点,提高算法的效率。
需要注意的是,SPFA算法适用于解决带有负权边的图的最短路径问题,但如果图中存在负权环,则算法无法得到正确的结果。因此,在使用SPFA算法之前,需要先判断图中是否存在负权环。(也可以使用此算法判断是否有负环)
二.算法的使用
题目:P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
spfa算法适用于存在负边权的情况,此题只是举例,最优解是Dijkstra算法http://t.csdn.cn/BxJwY
#include<bits/stdc++.h>
#define maxn 500010
using namespace std;
int n,m,s; //点,边,起点
int dis[maxn],vis[maxn],head[maxn];
//链式前向星
struct Edge{
int u,v,w,next;
}edge[maxn];
int cnt=0;
void add(int u,int v,int w){
edge[++cnt]=(Edge){u,v,w,head[u]};head[u]=cnt;
}
void spfa(){
queue <int> q;
for(int i=1;i<=n;i++) dis[i]=0x7fffffff; //初始化
dis[s]=0;vis[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v,w=edge[i].w;
if(dis[u]+w<dis[v]){
dis[v]=dis[u]+w;
if(!vis[v]){ //没入队就入队
q.push(v);vis[v]=1;
}
}
}
}
}
int main(){
//读入数据
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int u,v,w;cin>>u>>v>>w;add(u,v,w);
}
//调用算法
spfa();
//输出答案
for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
return 0;
}
三.判断负环
我们可以开一个数组in,存储一个点的入队次数。若它的入队次数>总点数(n),我们就可以认为它存在负环。
同P3371 【模板】单源最短路径(弱化版) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
但加入数据,若存在负环,则输出"No",
注意!现在改为无向图!
样例输入 :
3 3 1
1 2 3
2 3 4
3 1 -8样例输出:
No
参考代码:
/*
3 3 1
1 2 3
2 3 4
3 1 -8
*/
#include<bits/stdc++.h>
#define maxn 500010
using namespace std;
int n,m,s; //点,边,起点
struct Edge{
int u,v,w,next;
}edge[maxn<<1];
int dis[maxn],vis[maxn],head[maxn],in[maxn];
int cnt=0;
void add(int u,int v,int w){
edge[++cnt]=(Edge){u,v,w,head[u]};
head[u]=cnt;
}
bool flag=0;//判断负环
void spfa(){
queue<int> q;
for(int i=1;i<=n;i++) dis[i]=0x7fffffff;
dis[s]=0;
vis[s]=1;
in[s]++;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
vis[u]=0;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].v,w=edge[i].w;
if(dis[u]+w<dis[v]){
dis[v]=dis[u]+w;
if(!vis[v]){
q.push(v);vis[v]=1;in[v]++;
if(in[v]>n){
flag=1;
return;
}
}
}
}
}
}
int main(){
cin>>n>>m>>s;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
spfa();
if(flag){
cout<<"No"<<endl;
return 0;
}
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
return 0;
}