SPFA 算法是Bellman-ford算法的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。
前置知识:Bellman-ford算法详解_真的没事鸭的博客-CSDN博客
SPFA算法其实就是在Bellman-ford算法基础上的优化。Bellman-ford算法看起来比较傻,每次迭代的话是遍历所有边来更新,但是每次迭代的话不是每条边都会更新。SPFA算法就是对这个做优化,每次迭代dist[b]可以更新的话,一定是dist[a]变小了,因为如果dist[a]不变的话,dist[b]一定不变。只有dist[a]变小了,它的后继才会变小。所以SPFA算法就从这个点进行优化。
SPFA算法的思路就是迭代的时候用一个队列来做,队列里面存的就是到起点距离变小的点。先把起点放到队列里面去,只要队列不空,也就是队列里面还有距离变小的点的话,就执行一下操作:
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
- #include
- #include
- #include
- 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;
- }