最短路径问题:PAT A1003 Emergency(Dijkstra、Bellman-Ford、SPFA三种方法)

本文深入探讨了图论中三种关键的最短路径算法——Dijkstra算法、Bellman-Ford算法和SPFA算法。通过详细的代码实现和注释,为读者提供了理解和应用这些算法的全面指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

方法一:Dijkstra

//算法笔记 P378
//考点:Dijkstra 

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

const int MAXN=501;
const int INF=1000000000;
int n,m,st,ed,G[MAXN][MAXN],weight[MAXN];
int d[MAXN],w[MAXN],num[MAXN];
bool visit[MAXN]={false};

void Dijkstra(int s){
	fill(d,d+MAXN,INF);
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	d[s]=0;
	w[s]=weight[s];
	num[s]=1;
	for(int i=0;i<n;i++){
		int u=-1,MIN=INF;
		for(int v=0;v<n;v++){
			if(visit[v]==false&&d[v]<MIN){
				u=v;
				MIN=d[v];
			}
		}
		//如果未访问的点都与s不连通 
		if(u==-1) return ;
		visit[u]=true;
		for(int v=0;v<n;v++){
			if(visit[v]==false&&G[u][v]!=INF){
				if(d[u]+G[u][v]<d[v]){
					d[v]=d[u]+G[u][v];
					w[v]=w[u]+weight[v];
					num[v]=num[u]; 
				}else if(d[u]+G[u][v]==d[v]){
					if(w[u]+weight[v]>w[v]){
						w[v]=w[u]+weight[v];
					}
					num[v]+=num[u];
				}
			}
		}
	}
}


int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d%d",&n,&m,&st,&ed);
	fill(G[0],G[0]+MAXN*MAXN,INF);
	for(int i=0;i<n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v;
	for(int i=0;i<m;i++){
		scanf("%d%d",&u,&v);
		scanf("%d",&G[u][v]);
		G[v][u]=G[u][v];
	}
	Dijkstra(st);
	printf("%d %d",num[ed],w[ed]);
	return 0;
}

方法二:Bellman-Ford

//-----方法二---------------------------------------------- 
Bellman-Ford方法 
#include<bits/stdc++.h>
using namespace std;

const int MAXN=501;
const int INF=1000000000;
int n,m,st,ed,eight[MAXN];
int d[MAXN],w[MAXN],num[MAXN],weight[MAXN];
struct Node{
	int v,dis;
	Node(int _v,int _dis):v(_v),dis(_dis){
	};//构造函数 
};
vector<Node> Adj[MAXN];
set<int> pre[MAXN];
void Bellman(int s){
	fill(d,d+MAXN,INF);
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	d[s]=0;
	w[s]=weight[s];
	num[s]=1;
	bool flag=true;
	for(int i=0;i<n-1&&flag;i++){//执行n-1次操作 
		flag=false;
		for(int u=0;u<n;u++){//遍历所有点 
			for(int j=0;j<Adj[u].size();j++){//遍历每个点的所有边 
				int v=Adj[u][j].v;
				int dis=Adj[u][j].dis;
				if(d[u]+dis<d[v]){
					d[v]=d[u]+dis;
					//有边可以被松弛了 ,说明数组d中有值没有达到最优
					flag==true继续优化  
					flag=true;
					w[v]=w[u]+weight[v];
					num[v]=num[u];
					pre[v].clear();
					pre[v].insert(u);
				}else if(d[u]+dis==d[v]){
					if(w[u]+weight[v]>w[v]){
						w[v]=w[u]+weight[v];
                        //说明数组w中有值没有达到最优
					}
					pre[v].insert(u);
					//重新统计num[v]
					num[v]=0;
					set<int>::iterator it; 
					for(it=pre[v].begin();it!=pre[v].end();it++){
						num[v]+=num[*it];
					}
					//表示最短路径条数的num数组的num[v]可能发生改变
					//flag==true继续优化 
					flag=true;
				}
			} 
		}
	}
}


int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d%d",&n,&m,&st,&ed);
	for(int i=0;i<n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v,wt;
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&u,&v,&wt);
		Adj[u].push_back(Node(v,wt)) ;
		Adj[v].push_back(Node(u,wt));
	}
	Bellman(st);
	printf("%d %d",num[ed],w[ed]);
	return 0;
}

方法三:SPFA

//--------SPFA方法-------- ----------------
#include<bits/stdc++.h>
using namespace std;

const int MAXN=501;
const int INF=1000000000;
int n,m,st,ed,weight[MAXN];
int d[MAXN],w[MAXN],num[MAXN],cnt[MAXN]; 
bool inq[MAXN]={false};
set<int> pre[MAXN];
struct Node{
	int v,dis;
	Node(int _v,int _dis):v(_v),dis(_dis){
	};//构造函数 
};
vector<Node> Adj[MAXN];
bool SPFA(int s){
	fill(d,d+MAXN,INF);
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	queue<int> Q;
	Q.push(s);
	//s在队列里
	inq[s]=true;
	d[s]=0;
	w[s]=weight[s];
	num[s]=1;
	cnt[s]=1;//源点入队次数+1 
	while(!Q.empty()){
		int u=Q.front();
		Q.pop();
		inq[u]=false;
		for(int j=0;j<Adj[u].size();j++){
			int v=Adj[u][j].v;
			int dis=Adj[u][j].dis;
			if(d[u]+dis<d[v]){
				d[v]=d[u]+dis;
				w[v]=w[u]+weight[v];
				pre[v].clear();
				pre[v].insert(u);
				num[v]=num[u];
				if(!inq[v]){
				Q.push(v);
				//v的入队次数+1 
				cnt[v]++;
				inq[v]=true;
				}
			}else if(d[u]+dis==d[v]){
				if(w[u]+weight[v]>w[v]){
					w[v]=w[u]+weight[v];
				}
				pre[v].insert(u);
				num[v]=0;
				set<int>::iterator it;
				for(it=pre[v].begin();it!=pre[v].end();it++){
					num[v]+=num[*it];
				}
				//无论w[v]是否改变,num[v]都有可能改变,所以入队 
				if(!inq[v]){
				Q.push(v);
				cnt[v]++;
				inq[v]=true;
				}
			}
			//只要某一点入队次数>=n
			//则原图中有负环,则不可得到结果
			if(cnt[v]>=n) return false;
		}
	}
	return true;
}


int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d%d",&n,&m,&st,&ed);
	for(int i=0;i<n;i++){
		scanf("%d",&weight[i]);
	}
	int u,v,wt;
	for(int i=0;i<m;i++){
		scanf("%d%d%d",&u,&v,&wt);
		Adj[u].push_back(Node(v,wt));
		Adj[v].push_back(Node(u,wt));
	}
	bool ok=SPFA(st);
	if(ok) printf("%d %d",num[ed],w[ed]);
	else printf("有负环!"); 
	return 0;
}
 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值