SPFA算法详解

什么是SPFA算法

SPFA 算法是Bellman-ford算法的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环

前置知识:Bellman-ford算法详解_真的没事鸭的博客-CSDN博客

SPFA算法思路

SPFA算法其实就是在Bellman-ford算法基础上的优化。Bellman-ford算法看起来比较傻,每次迭代的话是遍历所有边来更新,但是每次迭代的话不是每条边都会更新。SPFA算法就是对这个做优化,每次迭代dist[b]可以更新的话,一定是dist[a]变小了,因为如果dist[a]不变的话,dist[b]一定不变。只有dist[a]变小了,它的后继才会变小。所以SPFA算法就从这个点进行优化。

SPFA算法的思路就是迭代的时候用一个队列来做,队列里面存的就是到起点距离变小的点。先把起点放到队列里面去,只要队列不空,也就是队列里面还有距离变小的点的话,就执行一下操作:

  1. 先取出队头t,然后队头出队
  2. 更新t的所有出边,t到起点的距离不是变小了吗, 那么所有和t相连的点都有可能变小,如果更新成功的话,就入队。但是注意要判断一下这个点已经入过队的话就不用重复加入了。

补充

SPFA算法的时间复杂度一般是O(m),最坏情况下时O(nm)。一般正权图我们是用Dijkstra算法去做,但是其实大部分的正权图的问题都可以用SPFA算法来做。不过出题人卡复杂度的话可能就用不了。所以SPFA算法是由Bellman-ford算法优化来的,不过长得特别像Dijkstra算法。

案例

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数

请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible

数据保证不存在负权回路。

输入格式

第一行包含整数 n 和 m。

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 impossible

数据范围

1≤n,m≤1e5
图中涉及边长绝对值均不超过 10000。

输入样例:

3 3
1 2 5
2 3 -3
1 3 4

输出样例:

2

来源:ACWing

 思路

这个和Dijkstra堆优化版的代码相似,就是用队列优化了一下。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N=1e6+10;
int h[N],e[N],ne[N],idx,w[N];//邻接表,w数组表示权重,idx是两个点之间的桥梁
int n,m;
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++;
}
int spfa()
{
    //初始化距离,1号点距离为0
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    //初始一个队列,用来存距离变小的点
    queue<int>q;
    q.push(1);//起点入队
    st[1]=true;
    while(q.size())//队列不空
    {
        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])//判断是否在队中,不在的话入队
                {
                    st[j]=true;
                    q.push(j);
                }
            }
        }
    }
    return dist[n];
}
int main()
{
    cin>>n>>m;
    //初始化h数组
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        //这里不考虑重边
        add(a,b,c);
    }
    if(spfa()==0x3f3f3f3f)
    cout<<"impossible";
    else
    cout<<spfa();
    return 0;
}
  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真的没事鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值