团体程序设计天梯赛——L2-001 紧急救援

团体程序设计天梯赛——L2-001 紧急救援

https://pintia.cn/problem-sets/994805046380707840/problems/994805073643683840

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。

输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。

输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。

输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例:

2 60
0 1 3

L1系列全部AJ,进入L2系列发现难度瞬间上升一个大档次,这道题是运用了我这学期刚刚学过的离散数学中图论部分的Dijkstra算法,原理我懂,但是感觉用程序来实现就难了一些,研究了好几个小时猜把这道题搞懂,果然我的道行还是太浅了,年后的假期要加把劲了呀!

首先是介绍Dijkstra算法,《啊哈!算法》这本书上有详细的介绍,在这里推一下大佬写的算法详解,真的是讲得好清楚:http://ahalei.blog.51cto.com/4767671/1387799

然后回归本题,要在Dijkstra算法之上增加救援队数量,增加最短路径的条数,也就是在Dijkstra算法求最小边权的前提下,求最大的点权,还要记录最小的边权有几种走法,以也就是最短路径的条数。本程序中,用path数组存放父节点,也就是到达这个点之前的点(这里可能有点绕口,就比如说0->1->2这样一条路,那么path[0]=0,path[1]=0,path[2]=1),所以输出path的时候也要用递归,从终点开始逆推直到起点,简单的for循环是实现不了的。quan数组存放所有节点的权值,dis数组存放起始点到每个节点的最短距离,per数组存放起始点到每个节点的最多救援队数量,book数组标记这个点有没有走过,sum数组存放起始点到这个节点的最短路径数,二维数组cost是整个地图的邻接矩阵。

弄清楚所有变量和数组的含义和作用后,仔细看下面的代码,注释写的很详细,只要花些心思理解起来应该不会太难。

(新手上路,有什么缺点不足还请大佬们多多指点,如果觉得本篇文章对您有帮助,就点个赞吼~)

(再遗憾的补充一小点吧,因为没有学习数据结构,所以后面的题啃起来太有难度,就暂停更新了,下学期学习数据结构的时候会边学习边做题更新的)

#include
#define INF 99999999//用来表示无穷大(也就是不可达)
#define MAX 505
using namespace std;
int N,M,S,D;//城市数量,道路数量,起始点,终点
int path[MAX],quan[MAX],dis[MAX],per[MAX],book[MAX],sum[MAX],cost[MAX][MAX];
//记录父节点数组,点权数组,最短路径,最多救援队数,这个点有没有被加入集合中,最短路径的条数,顶点之间边的关系

//从终点逆推到起点输出路径 
void printPath(int v){
  if (v==S){
    cout<<v;
    return;
  }
  printPath(path[v]);
  cout<<" "<<v;
}

int main(){
	cin>>N>>M>>S>>D;
	int s,d,l,min;//源点,目的点,长度,距离最小值 
	
	//进行cost数组的初始化(自己到自己的距离是0,其余全部不可达)
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			if(i==j)
				cost[i][j]=0;
			else
				cost[i][j]=INF;
		}
	}
	
	//输入每个城市的权值 
	for(int i=0;i<N;i++){
		cin>>quan[i];
	}
	
	//进行cost数组的输入 
	for(int i=0;i<M;i++){
		cin>>s>>d>>l;
		cost[s][d]=l;
		cost[d][s]=l;
	}
	
	//初始化dis数组,初始化book数组 
	for(int i=0;i<N;i++){
		dis[i]=cost[S][i];
		book[i]=0;
	} 
	per[S]=quan[S],sum[S]=1;//初始化起始救援队数量和最短路径条数 
	
	//Dijkstra算法 
	for(int i=0;i<N;i++){
		int u=-1;
		min=INF;
		//找到离源点S最近的点 
		for(int j=0;j<N;j++){
			if(book[j]==0&&dis[j]<min){
				min=dis[j];//更新最小值 
				u=j;
			}
		}
		if(u==-1) break;//如果u没有变化说明所有的点都遍历过了
		book[u]=1;//将这个最近的点加入集合中
		//更新dis和per数组 
		for(int v=0;v<N;v++){
			if(cost[u][v]!=INF&&book[v]==0){//如果可达 
				if(dis[v]>dis[u]+cost[u][v]){//且源点到其他点的距离大于通过新加的点中转再到其他点的距离 
					dis[v]=dis[u]+cost[u][v];//更新源点到其他点的距离,更新最短路径数组 
					per[v]=per[u]+quan[v];//更新源点到其他点的人数,更新最多救援队数数组
					path[v]=u;//记录父节点 
					sum[v]=sum[u];
				}else if(dis[v]==dis[u]+cost[u][v]){//且源点到其他点的距离等于通过新加的点中转再到其他点的距离
					sum[v]+=sum[u];//最短路径的条数 
					if(per[v]<per[u]+quan[v]){//距离一样,找到更多的救援队数 
						per[v]=per[u]+quan[v];//更新最多救援队数 
						path[v]=u;//记录父节点 
					}
				}
			}
		} 
	} 
	
	cout<<sum[D]<<" "<<per[D]<<endl;
	printPath(D);
} 

你可能感兴趣的:(团体程序设计天梯赛)