定义
给定一张有向图,若对于某一条边(x,y,z),有dist[y]<=dist[x]+z成立,则称该边满足三角形不等式。若所有边均满足三角形不等式,则dist数组就是所求的最短路。
基于迭代思想的Bellman-Ford算法
1.扫描所有边(x,y,z),若有dist[y]>dist[x]+z,则更新dist[y]
2.重复上述步骤直到没有更新操作发生
SPFA算法(队列优化的Bellman-Ford算法)
1.建立一个队列,最初只含有起点1
2.取出队头节点扫描所有出边(x,y,z),若有dist[y]>dist[x]+z,则更新dist[y]
同时,若y不在队列中则把y入队 //避免了对不需要拓展的节点的冗余扫描
3.重复上述步骤直到队列为空
Tips
1.一个节点可能会入队、出队多次
2.spfa适用于稀疏图,为O(km)级别,k是一个较小的常数
但在稠密图或特殊构造的网格图上可能退化为O(nm)
代码实现
//by ziwan Catherine
//spfa 边权可以为负
//d[n]从起点到n的最短路
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=10010,M=1000010;
int head[N],ver[M],edge[M],next[M],d[N];
bool v[N];
int n,m,tot;
queue<int> q;
void spfa(){
memset(d,0x3f,sizeof(d));
memset(v,0,sizeof(v));//是否在队列中
d[1]=0,v[1]=1;
q.push(1);
while(q.size()){
int x=q.front();q.pop();//取出队头
v[x]=0;
for(int i=head[x];i;i=next[i]){
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z){
d[y]=d[x]+z;
if(!v[y]) q.push(y),v[y]=1;
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
ver[++tot]=y,edge[tot]=z;
next[tot]=head[x],head[x]=tot;
}//构建邻接矩阵
spfa();
for(int i=1;i<=n;i++)
cout<<d[i]<<endl;
return 0;
}
判定负环
1.bellman-ford判负环
若经过n轮迭代算法仍未结束,说明存在负环。
在不存在负环的情况下,进行了n-1次所有边的更新操作后,每个节点的最短距离都确定了,再用所有边去更新一次不会改变结果。而如果存在负环,最后再更新一次会改变结果【原因是之前是假定了起点的最短距离是确定的并且是最短的,而又负环的情况下这个假设不再成立】
2.SPFA判负环
法一.设cnt[x]表示从1~x的最短路径包含的边数,cnt[1]=0。当执行更新d[y]=d[x]+z的操作时,同样更新cnt[y]=cnt[x]+1。
此时若发现cnt[y]>=n,则图中存在负环。
法二.【效率较低】记录每个点入队次数,达到n说明有负环。