SPFA算法(对bellman - ford的优化)(适用于负权边且不能有负权回路)
一般情况下spfa算法也可以解决正权边问题,且时间比Dijkstrsa更快(但是如果出题人卡掉了spfa算法,就只能用Dijkstrsa算法了)
上一篇博客介绍了bellman_ford算法,建议两个算法一起看
区别:bellman_ford算法更新每一个点,而spfa算法只更新变小的点。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int h[N] , e[N] , w[N] , ne[N] , idx;
int dist[N];
bool st[N];
void add(int a , int b , int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
void spfa()
{
queue<int>q;//存储每次更新的所有的点
dist[1] = 0;
q.push(1);
st[1] = true;
while(!q.empty())
{
int t = q.front();//每次从队列取出一个来更新所有它能够到达的点
q.pop();
st[t] = false;
for(int i = h[t] ; i != -1 ;i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];//更新操作
if(!st[j])//如果队列里面没有这个点就入队
{
q.push(j);
st[j] = true;
}
}
}
}
}
int main()
{
cin>>n>>m;
memset(dist,0x3f,sizeof dist);
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
spfa();
if(dist[n] == 0x3f3f3f3f) cout<<"impossible";
else cout<<dist[n];
return 0;
}
除此之外,spfa算法还可以用来判断是否存在负环。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;//n是点数,m是边数
int h[N] , e[N] , w[N] , ne[N] , idx;
int dist[N],cnt[N];
bool st[N];
void add(int a,int b, int c)
{
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++;
}
bool spfa()
{
queue<int>q;
//这里不需要求最短路,所以不需要更新初始值。
for(int i=1;i<=n;i++)//有可能从一号点到达不了负环,所以先将每个点都入队。
{
q.push(i);
st[i] = true;
}
while(!q.empty())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(cnt[j] >= n) return true;//如果有点入队的次数大于等于n,说明存在负权回路
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
if(!st[j])
{
q.push(j);
cnt[j] ++;//入队一次就将这个点入对的次数加一
st[j] = true;
}
}
}
}
return false;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=0;i<m;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
if(spfa()) cout<<"Yes";
else cout<<"No";
return 0;
}
总结spfa
- 初始化第一个点
- 更新每个点的节点