spfa是bellman-ford算法的一个优化,就是在bellman-ford算法基础上进行了广搜,怎么优化的呢?我们来看bellman-ford算法:n点m边
其实就是在枚举n次的时候枚举每个边,比如 存入两点以及边权时,
设有5次操作
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
那么初始化dist时就为 0 +∞ +∞ +∞+∞
第一次更新边 2 3 由于 dist[2]=+∞ dist[3]=+∞ dist[2]+2还是+∞所以未能松弛成功,枚举第二次 dist[1]=0,dist[2]=+∞ 所以成功更新dist[2]=-3;第二次枚举点的时候再次更新2 3因为此时dist[2]已经为-3,dist[3]=dist[2]+2=-1;所以可以完成松弛操作
那这样其实会有很多不必要的遍历边,所以我们遍历点的时候直接把和点相连的边如果它能更新当前点的值那么就把它加入队列,这样每次保证用到的都是操作的,提高效率
由于用到了邻接表所以看一下邻接表的操作
某伟把我的博客和别人的比了一番,所以我多加了很多注释
代码中注释↓
#include<iostream>
#include <vector>
#include<algorithm>
#include <queue>
#include<cstring>
using namespace std;
const int N = 100010,M=100010;
int n,m;
int h[N],w[M],e[M],ne[M],idx;
//h[N] 邻接表相当于是N个单链表,所以h[i]数组表示以点i为起点能到达的点
//w[M] 表示边的权值、
//e[M] 表示指针指向到达的点a➡b
//ne[M]表示h指向的上一个点
//idx 指针每次++
int q[N], 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++;//e存入下一个点,w存权值,ne存上一个点 h更新上一个点
}
int spfa()
{
queue<int> q;
memset(dist,0x3f,sizeof dist);
dist[1]=0;
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])
{
q.push(j);
st[j]=true;
}
}
}
}
return dist[n];
}
using namespace std;
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
int t=spfa();
if(t==0x3f3f3f3f)
cout<<"impossible"<<endl;
else
{
cout<<t<<endl;
}
return 0;
}