跳到主要内容

SPFA算法求最短路

AcWing 851. spfa求最短路

题目

https://www.acwing.com/problem/content/submission/853/ 给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。 请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 impossible。 数据保证不存在负权回路。

思路

SPFA是对Bellman_ford算法的优化

  • SPFA算法是由Bellman_ford算法优化而来,在最坏的情况下时间复杂度和它一样即时间复杂度为O(nm)
  • 求负环一般使用SPFA算法,方法是用一个cnt数组记录每个点到源点的边数,一个点被更新一次就+1,一旦有点的边数达到了n那就证明存在了负环。

算法执行步骤:

  • 建立一个队列,初始时队列里只有起始点。
  • 再建立一个数组记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。
  • 再建立一个数组,标记点是否在队列中。
  • 队头不断出队,计算始点起点经过队头到其他点的距离是否变短,如果变短且被点不在队列中,则把该点加入到队尾。
  • 重复执行直到队列为空。
  • 在保存最短路径的数组中,就得到了最短路径。

代码O(m)最坏O(nm)

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=100010;
int h[N],e[N],ne[N],w[N],idx;//邻接表存图
bool st[N];
int dist[N];
int n,m;

void add(int a,int b,int c)
{
w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

int spfa()
{
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;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;
}
}
}
}
return dist[n];
}


int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);

while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
int t=spfa();
if(t==0x3f3f3f3f) cout<<"impossible";
else cout<<t;
return 0;
}