Bellman-Ford与spfa算法

使用条件 

Bellman-Ford:存在负权边、可以有负环但必须同时要有最多经过k条边 (边

数限制)

SPFA:存在负权边、没有负环  99.9%的题没有负环

Bellman - ford 算法是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在 n-1 次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成

spfa算法的实现步骤 

1.实现spfa算法需要一个队列q,一个标记数组st[N]用来标记某点是否在队列中。数组dist[N],用   来存储起点到某个点的最短距离。
2.初始化dist数组为正无穷
3.从起点开始枚举每个点的所有子节点,设父节点到子节点的距离为s,父节点到起点的距离为dist[u],子节点到起点的距离为dist[v],如果dist[u]+s<dist[v]
当且仅当上式成立时就更新dist[v],如果v没有在队列中,就将v入队。

 

👉 解析在这👈

#include<bits/stdc++.h>
using namespace std;
const int N=510,M=10010;
int n,m,k;
int dist[N],backup[N];
struct edge{
	int a,b,w;
}e[M];

void bellman_ford(){
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	for(int i=0;i<k;i++){
		memcpy(backup,dist,sizeof dist);
		for(int j=0;j<m;j++){
			int a=e[j].a,b=e[j].b,w=e[j].w;
			dist[b]=min(dist[b],backup[a]+w);
		}
	}
}

int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<m;i++){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		e[i]={a,b,w};
	}
	bellman_ford();
	if(dist[n]>0x3f3f3f3f/2) puts("impossible");
	else cout<<dist[n]<<endl;
	return 0;
}

👉 ​​​​​详细解析在这👈

👉bellman_ford与spfa的对比👈​​​​​​​

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
int n,m;

void add(int a,int b,int c){//将边插入到邻接表中
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
void spfa(){
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	queue<int> q;
	q.push(1);//用起点去扩展其他点
	st[1]=true;//标记队列中已存在该点
	while(q.size()){
		int t=q.front();
		q.pop();
		st[t]=false;//t点已经出队
		for(int i=h[t];i!=-1;i=ne[i]){//i表示指向的位置
			int j=e[i];//j表示i指向位置的顶点值
			if(dist[j]>dist[t]+w[i]){ //w[i]表示该位置到它上一点权值
				dist[j]=dist[t]+w[i];
				if(!st[j]){//如果队列中现在没有该点
					q.push(j);
					st[j]=true;
				}
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof h);
	for(int i=0;i<m;i++){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		add(a,b,w);
	}
	spfa();
	if(dist[n]==0x3f3f3f3f) puts("impossible");
	else cout<<dist[n]<<endl;
	return 0;
}

👉解析​​​​​​​👈

#include<bits/stdc++.h>
using namespace std;
const int N=2010,M=10010;
int h[N],e[M],ne[M],w[M],idx;
int dist[N];
bool st[N];
int cnt[N];
int n,m;

void add(int a,int b,int c){
	w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

bool spfa(){
    // d 数组无需初始化,因为我们最终需要求的不是真正的距离
	queue<int> q;
	for(int i=1;i<=n;i++){//题中没有说明是连通图  不知道负环存在在哪个点上  所以把所有点全部加入到队列中  
		q.push(i);
		st[i]=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]){//因为负环的存在,所以即使每个点距离虚拟原点距离为0,
			                        //负权边的存在依然可以更新某个点为更小的负值 即不用初始化为无穷大
				dist[j]=dist[t]+w[i];
				cnt[j]=cnt[t]+1;
				if(cnt[j]>=n) return true;
		    	if(!st[j]){
			    	q.push(j);
			    	st[j]=true;
			}
			}
			
		}
	}
	return false;
}

int main(){
	memset(h,-1,sizeof h);
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);
	}
	if(spfa()) puts("Yes");
	else puts("No");
	return 0;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值