Bellman_ford算法会遍历所有的边,但是有很多的边遍历了其实没有什么意义,我们只用遍历那些到源点距离变小的点所连接的边即可,只有当一个点的前驱结点更新了,该节点才会得到更新;因此考虑到这一点,我们将创建一个队列每一次加入距离被更新的结点。
注意
1、vis并不是标记一下是否使用 而是标记是否可以再次压入queue。
2、跟堆优化Dijstra很像,但是完全不同。
3、Bellman_ford算法最后是用 dis[n] > inf/2 来判断而spfa是用 dis[n] == inf 判断 是因为bellman_ford会遍历每一个点,所以 n 点值会发生变化 而spfa每次都更新连通的源点之间距离,如果不连通是不会影响到 dis[n] 的。
4、Bellman_ford算法可以存在负权回路,是因为其循环的次数是有限制的因此最终不会发生死循环;但是SPFA算法不可以,由于用了队列来存储,只要发生了更新就会不断的入队,因此假如有负权回路就不要用SPFA,否则会死循环。
5、由于SPFA算法是由Bellman_ford算法优化而来,在最坏的情况下时间复杂度和它一样即时间复杂度为 O(nm)O(nm) ,假如题目时间允许可以直接用SPFA算法去解Dijkstra算法的题目。
6、求负环一般使用SPFA算法,方法是用一个cnt数组记录每个点到源点的边数,一个点被更新一次就+1,一旦有点的边数达到了n那就证明存在了负环。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long ll;
const int maxn = 1e5+10;
const int inf = 0x3f3f3f3f;
queue<PII> q ;
int w[maxn],to[maxn],vis[maxn],nex[maxn],now[maxn],dis[maxn];
// 权值 边终点 标记 同一起点的 目前编号 到达每一点
// 另一边编号 的最小距离
int idx,n,m;
void add(int x,int y,int z){
to [idx] = y;
w [idx] = z;
nex[idx] = now[x];
now[x] = idx++;
}
int di(){
q.push({0,1}); //压入 距离0 端点1
dis[1] = 0; //起点1 距离0
vis[1] = 1;
while(!q.empty()){
PII k = q.front();
q.pop();
int distance = k.first , ver = k.second;
vis[ver] = 0;
for(int i = now[ver]; i != -1 ; i = nex[i]){ //同起点 不同边
int j = to[i];
if(dis[j] > dis[ver] + w[i]){
dis[j] = dis[ver] + w[i];
if(!vis[j]){
vis[j]=1;
q.push({dis[j],j});
}
}
}
}
if(dis[n]==inf) cout<<"impossible";
else cout<<dis[n];
}
int main(){
fill(now,now+maxn,-1);
fill(dis,dis+maxn,inf);
cin>>n>>m;
for(int i = 0 ;i < m; i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
}
di();
}