贝尔曼-福特最短路算法及其优化(SPFA)

贝尔曼-福特最短路算法及其优化(SPFA)

日常膜拜dalao:财神万岁!!!!!!!!!!!!!!!!!!!!!!!
日常凌晨水题解。。。(我发誓这是我今天最后一篇博客了。。)
话说,,我的博客一般解释全在代码注释里面,,而且讲的不细致。真的想详细看模拟的话参见dalao的博客:https://blog.csdn.net/Martisum/article/details/95939270

贝尔曼-福特算法

为什么我们不用Dijkstra算法?
因为,Dijkstra无法处理负权边的状况(这个我这篇博客不细讲)。而贝尔曼—福特算法可以很优秀的解决这个问题;(而且贝尔曼福特很好理解)
用dis数组来表示每个点到原点的距离,w 数组来表示每条边的权值,u数组表示每条边的起点,v数组表示每条边的终点。
贝尔曼-福特算法的主要思想是用边来进行更新,将所有的点循环一遍,再将所有的边循环一遍,每次看是否能用边成功的将一个点的距离更新(初始除了原点每个点的dis都是inf)
当存在负权回路时,两层循环完后仍然有点能够被更新。
话不多说,上代码:

#include<iostream>//贝尔曼—福特(bellman_ford)
#define inf 0x3f3f3f 
using namespace std;
/*此算法的思想:用边来进行更新,进行n-1次循环,每次枚举一条边,
如果这条边的dis[v]>dis[u]+w[i]则对其进行更新,两层循环完后,
如果还有能更新临点的边,则存在负权回路 */ 
	int m,n;
	int dis[100000];
	int u[2000],v[2000],w[2000];
void belman(int s)//s是源点 
{
	for(int i=1;i<=n;i++) dis[i]=inf;
	dis[s]=0;
	for(int i=1;i<=n-1;i++)//一个点到源点最短路一定最多经过n-1条边 
	{
		for(int j=1;j<=m;j++)
		{
			dis[v[i]]=min(dis[v[i]],dis[u[i]]+w[j]);
		}
	}
	flag=false;
	for(int i=1;i<=m;i++)//若有点两重循环完了以后还能够被更新, 
	{//则一定存在负权回路 
		if(dis[v[i]]>dis[u[i]]+w[i])
		flag=false;i//存在负权回路 
	}
}
int main()
{
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	{
		cin>>u[i]>>v[i]>>w[i]; 
	}
	belman(1);
 } 

然而,贝尔曼福特算法的复杂度比迪杰斯特拉还要差,O(n*m)。
所以就有了通过队列对贝尔曼福特进行优化的SPFA算法

SPFA

利用队列存放被更新的点,显而易见,只有一个被更新过的点才能被用来更新其他的点(原点除外),将被更新的点扔到队列里面,每次取出队首的点看是否能够用来更新其临点,注意:与广度优先搜索不同的是,一个点可能入队很多次,队列空了算法结束。
本代码使用了stl队列,如果不懂stl,手写队列也可以

#include<iostream>
#include<queue>
#include<cstring>
#define maxn 1005
#define maxm 2005
#define inf 0x3f3f3f
/*利用队列对福特算法进行优化,将被更新的点放到队列种,
每次取出队列前面的点并对其临点进行更新,若被更新则可用来再次
更新其临点,与广搜不同的是,一个点可能入队很多次。当队列空了,
算法结束。*/ 
using namespace std; 
struct node
{
	int next;
	int to;
}ed[maxm];	
queue<int> p;
int head[maxn],inque[maxn],dis[maxn];
int m,n,cnt=0;
void addnode(int u,int v,int w)
{
	cnt++;
	ed[cnt].to = v;
	ed[cnt].worth = w;
	ed[cnt].next =head[u];
	head[u]=cnt;
}
void spfa(int s)
{
	for(int i=1;i<=n;i++) dis[i]=inf;
	dis[s]=0;
	inque[s]=1;
	p.push(s);
	while(!p.empty())
	{
		int nod=p.front();
		p.pop();
		inque[nod]=0;
		for(int i=head[nod];i;i=ed[i].next )
		{
			if(dis[ed[i].to ]>(dis[nod]+ed[i].worth))
			{
				dis[ed[i].to]=dis[nod]+edd[i].worth;
				if(inque[ed[i].to]==0)
				{
					p.push(ed[i].to);
					inque[ed[i].to]=1;
				}
			}
		} 		
	}
}
int main()
{

	cin>>m>>n;
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		addnode(a,b,c);
		addnode(b,a,c);	
	}	
	memset(inque,0,sizeof(inque));
	spfa(1);
	return 0; 
 } 

本文如有不周到处欢迎评论,
感谢各位神犇对本蒟蒻的支持!qwq

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值