PAT1003,最短路径,dijkstra代码详解(逐行注解)

PAT1003,最短路径,dijkstra代码详解(逐行注解)

dijkstra

dijkstra详解:
链接: Dijkstra算法详细讲解——逆风行砾.

1.解决问题:单源最短路径问题
2.主要情况:①双向边与单项边(通过双向赋值实现);②需要输出最短路径(通过前驱节点实现);③每条边有边权(支出),需要输出最小花费;④每个节点有点权(收获),需要输出最大收益;⑤需要输出最小路径条数
3.具体示例代码详解
(本示例中仅对点权与最小路径条数进行了输出,若需要输出最小花费,自行添加每条边的支出情况并保存到cost[][]中,并选择相应的语句取消注释即可)

示例输入:
第一行分别为:节点个数n,边数m,起始点c1,终点c2
第二行为:节点权重
接下去m行为:每条边的起始点a1,终点a2,边距离a3,边权a4

5 6 0 2
1 2 1 5 3
0 1 1 0
0 2 2 1
0 3 1 2
1 2 1 3
2 4 1 4
3 4 1 5

示例输出:
分别为第i个节点的:i ,最短路径长度,最短路径数量,最大收获,最小损失,DFS(最短路径途径的节点,若路径长度相同,则收获大的优先),DFS输出可根据自己需要进行调整,若需要调整为路径相同,损失小的优先,将前驱节点更新代码加入到损失更新中即可。(代码中已给出并注释)

i:0 1 0 1 0
DFS:0
i:1 1 1 3 0
DFS:0 1
i:2 2 2 4 1
DFS:0 1 2
i:3 1 1 6 2
DFS:0 3
i:4 1 2 9 7
DFS:0 3 4

具体代码:

/* 
Author:纳兹米z
Reference:算法笔记——胡凡
*/ 
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxv = 510;   //最大节点数 
const int INF = 1000000000; //最大距离初始化 INT_MAX 
int n,m,c1,c2;  //n:节点个数   m:边数   c1:起始点  c2:终点 
int G[maxv][maxv];//定义图
bool vis[maxv] = {0};  //访问情况 
int d[maxv];   //去每个节点的最短路径 

//点权 
int weight[maxv]; //每个节点的权重 
int w[maxv] ;    //以最短路径去每个节点的最大收获 

//最短路径条数 
int num[maxv];//最短路径条数 

//边权 
int cost[maxv][maxv];
int c[maxv]; 

//前驱节点 
int pre[maxv];   //每个节点的前驱节点 

//递归输出整条路径 
void DFS(int s,int v){
	if(v==s){
		printf("%d ",s);
		return;
	}
	DFS(s,pre[v]);
	printf("%d ",v);
}

int dijkstra(int s){
	fill(d,d+maxv,INF);   //初始化最小距离:INF 
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	fill(c,c+maxv,INF); 
	d[s] = 0;    //起始点距离为0 
	w[s] = weight[s];   //起始点权重为本点权重 
	c[s] = 0;    //起始点花销为0 
	num[s] = 1;
	for(int i = 0;i<n;++i){
		int u = -1,MIN = INF;
		for(int j = 0;j<n;++j){       //寻找距离最近的点 
			if(vis[j]==0&&d[j]<MIN){  //如果未访问并且距离比最小距离近 
				u = j; 
				MIN = d[j];
			}
		}
		if(u == -1) return 0;  //找不到联通的顶点 
		vis[u] = 1;       //找到最近点u,访问 
		for(int v = 0;v<n;++v){   //寻找最近点u的相邻点v 
			if(vis[v] == 0&&G[u][v]!=INF){   //相邻点v未访问并且有路径 
				if((d[u]+G[u][v])<d[v]){      //通过u点访问v点的路径比其原最短路径短 
					d[v] =d[u]+G[u][v];     //更新v的最短路径 
					w[v] = w[u]+weight[v];  //更新v的收获
				    c[v] = c[u]+cost[u][v]; //更新路上损失 
					num[v] = num[u];        //v的条数继承u的条数 
					pre[v]=u;               //v的前驱为u 
				}
				
				else if((d[u]+G[u][v])==d[v]){  //最短路径相等 
					num[v]+=num[u];            //最短路径条数相加(即原来有num[v]条最短路径(不经过u点),加上经过u点的最短路径num[u]为新的最短路径 条数 
				
					if((w[u]+weight[v])>w[v]){  //最短路径相同且收获小于原最短路径的收获 
						w[v] = w[u]+weight[v];	  //更新最大收获 
						pre[v]=u;               //v的前驱为u 此处设置了若最短路径相同,收益大的为对应节点前驱 
					}
					
					if(c[v]>c[u]+cost[u][v]){  //最短路径相同且损失小
						c[v] = c[u]+cost[u][v]; //更新路上损失
//						pre[v]=u;               //v的前驱为u 此处设置了若最短路径相同,收益大的为对应节点前驱 
					} 
				}
							
			}
		}
	}	
}
int main(){
	cin>>n>>m>>c1>>c2;   //输入:节点个数n,边数m,起始点c1,终点c2 
	for(int i = 0;i<n;++i){
		cin>>weight[i];    //输入点权 
	}
	int a1,a2,a3,a4;
	fill(G[0],G[0]+maxv*maxv,INF);
	for(int i = 0;i<m;++i){
		cin>>a1>>a2>>a3>>a4;   //输入a1(节点) a2(节点) a3(两节点间距离) 
		G[a1][a2] = a3;   //单向边 a1 到 a2的距离为a3 
		G[a2][a1] = a3;   //双向边 a2 到 a1的距离为a3 若为单向边,将此条注释即可 
		cost[a1][a2] = a4;//单向边 a1 到 a2的边权为a4 
		cost[a2][a1] = a4;//双向边 a2 到 a1的边权也为a4 若为单向边,将此条注释即可 
	}
	dijkstra(c1);   //调用dijkstra  获取每条边的最短路径 
	for(int i = 0;i<n;++i){   //打印每条边的信息 
		cout<<"i: "<<i<<"  "<<num[i]<<" "<<w[i]<<" "<<c[i]<<endl;
		cout<<"DFS:"; 
		DFS(c1,i);
		cout<<endl;
	}
} 

PAT1003


在这里插入图片描述
题目分析:
(1)标尺:题目有两个标尺,第一标尺为距离,第二标尺为收益。
(2)题目关键点:①双向边;②需输出最短路径条数;③含有点权,需输出最大收益

具体代码:

/* 
Author:纳兹米z
Reference:算法笔记——胡凡
*/ 
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxv = 510;   //最大节点数 
const int INF = 1000000000; //最大距离初始化 INT_MAX 
int n,m,c1,c2;  //n:节点个数   m:边数   c1:起始点  c2:终点 
int G[maxv][maxv];//定义图(存储每条边的距离)
bool vis[maxv] = {0};  //访问情况 
int d[maxv];   //去每个节点的最短路径 
//点权 
int weight[maxv]; //每个节点的权重 
int w[maxv] ;    //以最短路径去每个节点的最大收获 
//最短路径条数 
int num[maxv];//最短路径条数 

int dijkstra(int s){
	fill(d,d+maxv,INF);   //初始化最小距离:INF 
	memset(w,0,sizeof(w));
	memset(num,0,sizeof(num));
	d[s] = 0;    //起始点距离为0 
	w[s] = weight[s];   //起始点权重为本点权重 
	num[s] = 1;
	for(int i = 0;i<n;++i){
		int u = -1,MIN = INF;
		for(int j = 0;j<n;++j){       //寻找距离最近的点 
			if(vis[j]==0&&d[j]<MIN){  //如果未访问并且距离比最小距离近 
				u = j; 
				MIN = d[j];
			}
		}
		if(u == -1) return 0;  //找不到联通的顶点 
		vis[u] = 1;       //找到最近点u,访问 
		for(int v = 0;v<n;++v){   //寻找最近点u的相邻点v 
			if(vis[v] == 0&&G[u][v]!=INF){   //相邻点v未访问并且有路径 
				if((d[u]+G[u][v])<d[v]){      //通过u点访问v点的路径比其原最短路径短 
					d[v] =d[u]+G[u][v];     //更新v的最短路径 
					w[v] = w[u]+weight[v];  //更新v的收获
//				    c[v] = c[u]+cost[u][v]; //更新路上损失 
					num[v] = num[u];        //v的条数继承u的条数 
				}
				
				else if((d[u]+G[u][v])==d[v]){  //最短路径相等 
					num[v]+=num[u];            //最短路径条数相加(即原来有num[v]条最短路径(不经过u点),加上经过u点的最短路径num[u]为新的最短路径 条数 
				
					if((w[u]+weight[v])>w[v]){  //最短路径相同且收获小于原最短路径的收获 
						w[v] = w[u]+weight[v];	  //更新最大收获 
					}
				}
							
			}
		}
	}	
}
int main(){
	cin>>n>>m>>c1>>c2;   //输入:节点个数n,边数m,起始点c1,终点c2 
	for(int i = 0;i<n;++i){
		cin>>weight[i];    //输入点权 
	}
	int a1,a2,a3;
	fill(G[0],G[0]+maxv*maxv,INF);
	for(int i = 0;i<m;++i){
		cin>>a1>>a2>>a3;   //输入a1(节点) a2(节点) a3(两节点间距离) 
		G[a1][a2] = a3;   //单向边 a1 到 a2的边权为a3 
		G[a2][a1] = a3;   //双向边 a2 到 a1的边权也为a3 若为单向边,将此条注释即可 
	}
	dijkstra(c1);   //调用dijkstra  获取每条边的最短路径 
	cout<<num[c2]<<" "<<w[c2]<<endl;
} 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值