【SPFA】

SPFA算法

简介:

SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。

算法的实现步骤

实现spfa算法需要一个队列q,一个标记数组vis[N]用来标记某点是否在队列中。数组dist[N],用来存储起点到某个点的最短距离。

  1. 初始化dist数组为正无穷
  2. 从起点开始枚举每个点的所有子节点,设父节点到子节点的距离为w,父节点到起点的距离为dist[u],子节点到起点的距离为dist[v],如果
    dist[u]+w>dist[v]
  3. 当且仅当上式成立时就更新dist[v],如果v没有在队列中,就将v入队。

例子:

给出一个有向图,请输出从某一点出发到所有点的最短路径长度
输入格式

第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 mm 行每行包含三个整数 u,v,w,表示一条 u→v 的,长度为 w 的边。

输出格式
输出一行 nn个整数,第 ii 个表示 ss 到第 ii 个点的最短路径,若不能到达则输出-1

输入样例
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出样例
0 2 4 3

  1. 首先初始化,将vis[]赋值为0,dis[]赋值为无穷。然后将起点1入队,将vis[1]=1,dis[1]=0。
  2. 将队列的头元素1从队列中取出,遍历他的子节点(2,3,4),由于dis[2]>dis[1]+2,更新
    dis[2]=dis[1]+2,以此类推,更新dis[3]=dis[1]+5,dis[4]=dis[1]+4,将(2,3,4)入队,vis[2]=1,vis[3]=1,vis[4]=1

在这里插入图片描述
3.将队列中的头元素2取出,并将vis[2]=0,遍历2的子节点(3,4)。
由于dis[3]>dis[2]+2,dis[3]=dis[2]+2,由于vis[3]=1,即3在队列中,就不用入队。
由于dis[4]>dis[2]+1,dis[4]=dis[2]+1,由于vis[4]=1,即4在队列中,就不用入队。
在这里插入图片描述
4. 将队列中的头元素3取出,并将vis[3]=0,遍历3的子节点(4)。
由于dis[4]<dis[3]+3,不用更新。
在这里插入图片描述
5. 将队列中的头元素4取出,并将vis[4]=0,由于4没有子节点,不进行遍历,此时队列为空,结束循环,dis[i]即为1到i的最短路
在这里插入图片描述
再此贴上本题代码

#include<bits/stdc++.h>
const long long inf=2147483647;
const int maxn=10005;
const int maxm=500005;
using namespace std;
int n,m,s,cnt=0;
int dis[maxn],vis[maxn],head[maxm];
struct Edge
{
  int next,v,w;
}edge[maxm]; //结构体表示静态邻接表
void addedge(int u,int v,int w) //邻接表建图
{ edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt; 
}
void spfa()
{
  queue<int> q; 
  for(int i=1; i<=n; i++) 
  {
    dis[i]=inf; //带权图初始化
    vis[i]=0; //记录点i是否在队列中,
  }
  q.push(s); dis[s]=0; vis[s]=1; //起点入队,进行标记
  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; 
      if(dis[v]>dis[u]+edge[i].w) //如果有最短路就更新
      {
        dis[v]=dis[u]+edge[i].w;
        if(vis[v]==0) //未入队则入队
        {
          vis[v]=1; //标记入队
          q.push(v);
        }
      }
    }
  }
}
int main()
{
  cin>>n>>m>>s;
  for(int i=1,u,v,w; i<=m; i++)
  {
    cin>>u>>v>>w; 
    addedge(u,w,w);
  }
  spfa();
  for(int i=1; i<=n; i++)
    if(s==i) cout<<0<<" "; //如果是回到自己,直接输出0
      else cout<<dis[i]<<" "; //否则打印最短距离
  return 0;
} 

题目练习:

https://www.luogu.com.cn/problem/P1073
https://www.luogu.com.cn/problem/P1342

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值