Bellman-Ford和SPFA算法

定义

    给定一张有向图,若对于某一条边(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说明有负环。

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/zi_wan/article/details/80910161
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞