PAT真题(C语言)——1003:Emergency

题目如下:
PAT真题(C语言)——1003:Emergency_第1张图片
这道题是一道典型的求最短路径,我采用的是Dijkstra最短路径算法。
和传统的最短路径不同的是,这道题里的节点间举例不是用一个N*N矩阵表示,而是分别列出了每条路径的起始城市、终点城市和路径长度。
这道题花了我不少时间,主要调了很久的bug终于调通,先把整个流程大致讲一下,例子就用题目中给出的例子。传统的Dijkstra算法的流程如下:
下表中,最左边一列代表不同的终点,表格内容中的二维向量分别代表起点到该点的最短距离和最短路径的上一节点编号。第一步是初始化与起点直接相邻的城市,因此初始化结果如下图,其中,城市4由于和城市0不直接相邻,因此用–代替。:
PAT真题(C语言)——1003:Emergency_第2张图片
第二步:选择第一步中距离起点城市最近的城市,并判断经由该城市到达其它城市是否存在更短的路径,如选择城市1(也可以选择城市3,它们目前距离都为1),如果存在一条路径0-1-2的距离比0-2的距离更近,则更新表内容,否则不变,因此第二步后的结果为:
PAT真题(C语言)——1003:Emergency_第3张图片
第三步则在第二步的基础上重复上述过程(选择城市3),注意此处城市4有了距离,且路径中的上一城市为城市3,结果为:
PAT真题(C语言)——1003:Emergency_第4张图片
最后一步同上:
PAT真题(C语言)——1003:Emergency_第5张图片
这就是最终结果,根据该表,可以查询到从0出发到任意城市的最短距离和最短路径,如0到4,根据最后一项可知,最短距离为2,路径的上一节点为3,然后查阅3中最后一项可知,上一节点是0。因此存在路径0-3-4是最短路径。

以上方式是传统方法,是解决这一问题的基础,但只用以上的数据还不足以解决该问题,因为还涉及到救援队数量、最短路径条数等,但可以用相同的方法将这些变量加入进去,最终我的代码如下:


#include 
#include 

#define PATH 10000
#define MAX_CITY 10000
#define INF 0xFFFFFFF
#define true 1
#define false 0

int main() {
	int numOfCity, numOfPath, start, end;
	int resNum[MAX_CITY] = { 0 };
	int a[PATH][3];

	int visited[MAX_CITY] = { 0 };//to record that the i-th city is visited or not
	int maxRes[MAX_CITY] = { 0 };//to record the max number of rescue teams start with city 0 to the i-th city
	int lenToICity[MAX_CITY] = { 0 };//to record the min length to i-th city start with city 0
    int numOfShortestPath[MAX_CITY] = { 0 };
	int curMincity;


	scanf("%d %d %d %d", &numOfCity, &numOfPath, &start, &end);
	for (int i = 0; i<numOfCity; i++) {//number of rescue teams in the i-th city
		scanf("%d", &resNum[i]);
	}
	for (int i = 0; i<numOfPath; i++) {
		scanf("%d %d %d", &a[i][0], &a[i][1], &a[i][2]);
	}

	//array init
	visited[start] = true;
	for (int i = 0; i < numOfCity; i++) {
		lenToICity[i] = INF;
	}

	for (int i = 0; i<numOfPath; i++) {//init the lenth to cities nearby and the Max number of rescue teams 
		if (a[i][0] == start) {
			lenToICity[a[i][1]] = a[i][2];
			maxRes[a[i][1]] = resNum[start] + resNum[a[i][1]];
            numOfShortestPath[a[i][1]]++;
		}
		else if (a[i][1] == start) {
			lenToICity[a[i][0]] = a[i][2];
			maxRes[a[i][0]] = resNum[start] + resNum[a[i][0]];
            numOfShortestPath[a[i][0]]++;
		}
	}

	while (1) {
		curMincity = start;
		for (int i = 0; i<numOfCity; i++) {//choose the closest city 
			if (visited[i] == false && lenToICity[i] != 0 && lenToICity[i]<lenToICity[curMincity]) {
				curMincity = i;
			}
		}
		if (curMincity == start)//which means every city was visited
			break;

		visited[curMincity] = true;

		for (int i = 0; i<numOfPath; i++) {
			if (a[i][0] == curMincity) {
				if (visited[a[i][1]] == false) {
					if (lenToICity[a[i][1]] >(lenToICity[curMincity] + a[i][2])) {
						lenToICity[a[i][1]] = (lenToICity[curMincity] + a[i][2]);
						maxRes[a[i][1]] = maxRes[curMincity] + resNum[a[i][1]];
                        numOfShortestPath[a[i][1]] = numOfShortestPath[curMincity];
					}
					else if (lenToICity[a[i][1]] == (lenToICity[curMincity] + a[i][2])) {//之前出错
                        numOfShortestPath[a[i][1]] += numOfShortestPath[curMincity];
						if (maxRes[a[i][1]] < maxRes[curMincity] + resNum[a[i][1]]) {
							maxRes[a[i][1]] = maxRes[curMincity] + resNum[a[i][1]];
						}
					}
				}
			}

			else if (a[i][1] == curMincity) {
				if (visited[a[i][0]] == false) {
					if (lenToICity[a[i][0]] > (lenToICity[curMincity] + a[i][2])) {
						lenToICity[a[i][0]] = (lenToICity[curMincity] + a[i][2]);
						maxRes[a[i][0]] = maxRes[curMincity] + resNum[a[i][0]];
                        numOfShortestPath[a[i][0]] = numOfShortestPath[curMincity];
					}
					else if (lenToICity[a[i][0]] == (lenToICity[curMincity] + a[i][2])) {//之前出错
						numOfShortestPath[a[i][0]] += numOfShortestPath[curMincity];   
                        if (maxRes[a[i][0]] < maxRes[curMincity] + resNum[a[i][0]]) {
							maxRes[a[i][0]] = maxRes[curMincity] + resNum[a[i][0]];
						}
					}
				}
			}
		}

	}

    numOfShortestPath[start] = 1;
    maxRes[start] = resNum[start];

	printf("%d %d", numOfShortestPath[end], maxRes[end]);

	return 0;
}

代码整体比较复杂,还不够精炼,下面说说我一开始调不通主要遇到的问题:
1、读题读错了,以为输出的第一项是最短路径长度,结果是最短路径条数,最搞的是输出错了的情况下,第一条测试点居然过了……
2、在代码中有两个地方我注释了“之前错了”是一个比较大的问题来源,在之前我写的不是else if,而是if,这样导致的问题就是,在这上面的if执行完毕以后还会执行一次下面的if,然而我是不希望他们同时执行,而是应该择一执行,总结出的一大教训就是以后遇到希望择一执行的时候,尽量用else if而不是多个if并列;
3、这个问题是我测试点2没有通过的原因,查了好久,看了别人的代码后才发现,如果出发城市和终点城市是同一个,那么路径条数是1,我之前一直初始化的0……气

另外提一下,我这种做法实际上是很浪费时间的,因为当遍历过了终点以后就没必要继续往下了,即如果我在某一步选择的是终点的话,那么接下来就没必要继续了,可以直接跳出循环了,这样效率能够大大提高。对应就只需要在while循环开始几行里,把下面代码:

if (curMincity == start)//which means every city was visited
			break;

改成:

if (curMincity == start || curMincity == end)//which means every city was visited
			break;

就可以了。
总之,这道题完全跑通花了我好多时间,还需要继续努力,各种小错误还是太多了,很影响效率,嗯!加油吧!

你可能感兴趣的:(PAT)