PAT甲级 1003 Emergency (25分)题目理解以及测试点的易错点

题目介绍

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:
Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c​2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C​1 to C2.

Output Specification:
For each test case, print in one line two numbers: the number of different shortest paths between C​1​​ and C​2​​ , and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input:

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

Sample Output:

2 4

简单来说就是,一个无向图,每个点有编号也有权值,边也有权值。需要你找出两点之间最短路径的个数以及最短路径上所经过的点的点权之和的最大值。后一个可能有点难理解,意思就是边权最短的路径可能有多条,它们经过的点可能不一样,你要找到最短路径里面点权之和最大的那条路径,注意!把它的值找到就行,不用你把具体路径找出来。

题目以及测试点的易错点

一号易错点: 如果图只有一个点,或者出发点和目的点都是同一个点,这个情况需要考虑!如输入

1 0 0 0
2

应该输出1 2,如果不注意这点,代码往往会输出0 0,那样就会有几个测试点过不了

二号易错点: 因为最短路径可能有多条,所以要记得有的路径的分支点在中间,也就是要记得计算路径中从出发点到该点的最短路径个数。比如有的路径长这样:

1
1
1
1
1
0
1
2
3
4

其中0号点到3号点有两条最短路径,所以虽然3号点到4号点只有一条最短路径,但是0号点到4号点有两条最短路径。这个其实很容易想到,但是写代码的时候容易犯迷糊,容易只在最后找到到达目标点的新路径后才去做判断,这样往往就容易漏掉中间点其实也有两条最短路径的情况。

三号易错点: 注意输出的第二个数是最短路径上所经过的点的点权之和的最大值,所以不要误找为两点间所有路径中所经过的点的点权之和的最大值。这就要求我们在每次遇到边权值相同的不同路径时都要计算一下这条路径的点权之和是否比之前记录的点权之和要大。

四号易错点: 图是无向图,所以使用dijkstra算法的时候要记得,边的两端是可以相互到达的。

代码

#include<iostream>
#include<vector>
#include<map>
#include"limits.h" //要用到INT_MAX,需要包含这个头文件
using namespace std;

int main(){
	int cs,rs,now,wg;//点的数量,边的数量,出发点,目的点
	cin>>cs>>rs>>now>>wg;
	int *teams = new int[cs];//点的权重
	vector<vector<int>> roads;//图的邻接矩阵
	roads.resize(cs);
	for(int i=0;i<cs;i++){
		cin>>teams[i];
		roads[i].resize(cs);
		for(int j=0;j<cs;j++)
			roads[i][j] = INT_MAX;//默认边权为INT_MAX,表示不可到达
	}
	if(now==wg)cout<<1<<" "<<teams[now];//若是出发点与目的点相同,直接输出(一号易错点)
	else{
		int a,b,w;
		for(int i=0;i<rs;i++){    //输入存在的边
			cin>>a>>b>>w;
			roads[a][b] = w;
			roads[b][a] = w;
		}
		vector<int> rocount;//出发点到达某点的最短路径数量
		rocount.resize(cs);
		for(int i=0;i<cs;i++){
			if(roads[now][i]!=INT_MAX) rocount[i] = 1;
			else rocount[i] = 0;
		}
		vector<int> weight;//出发点到达某点路径上经过的点(包括出发点和该点)的点权之和
		weight.resize(cs);
		for(int i=0;i<cs;i++){
			if(roads[now][i]!=INT_MAX) weight[i] = teams[now] + teams[i];
			else weight[i] = 0;
		}
		map<int,int> check;//出发点到达某点的最短路径权值
		for(int i=0;i<cs;i++)check[i] = roads[now][i];
		int i = check.erase(now);
        while(!check.empty()){  //改动的dijkstra算法
			int searm=INT_MAX,n;
            map<int,int>::iterator iter;
			for(iter = check.begin();iter!=check.end();iter++){   //找出当前没走过的最短边和该边通向的点
				if(searm>iter->second){
					searm = iter->second;
					n = iter->first;
				}
			}
			if(n == wg)//如果该最短边通向的点正是目的点,直接退出
				break;
			for(int i =0;i<cs;i++){  //遍历该最短边通向的点的边
				if(roads[n][i]!=INT_MAX){
					iter = check.find(i);
					if(iter!=check.end()){
						int len = iter->second;
						if(len>(roads[n][i]+searm)){  //如果记录中到这一点的最短距离还没有从刚刚找到的点到它的最短距离短
							rocount[i]=rocount[n];  //注意这里要把最短路径数量设置成我们找到的点的最短路径数量(二号易错点)
							weight[i] = weight[n] + teams[i];  //注意这里无论之前的点权之和有多大都要重置成现在找到的最短路径的点权之和(三号易错点)
							check[i] = roads[n][i]+searm;
						}
						if(len==(roads[n][i]+searm)){  //如果记录中到这一点的最短距离和从刚刚找到的点到它的最短距离一样短
							rocount[i] = rocount[i] + rocount[n];  //原来的最短路径数加上刚刚找到的点的最短路径数
							if(weight[i] < weight[n] + teams[i])  //要是之前记录的点权之和没有刚刚找到的这条路径的点权之和大就要修改
								weight[i] = weight[n] + teams[i];
						}
					}
				}
			}
			i = check.erase(n);//将刚刚找到的这条边在map中删除表示为已经走过了
		}
		cout<<rocount[wg]<<" "<<weight[wg];
	}
}

本人刚学写代码,真心求各位对我的代码和解题思路提出宝贵的建议,希望这篇文章能帮助到和我一样的初学者,大家一起进步!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值