PAT 甲级刷题实录——1003

本贴最后更新于 1807 天前,其中的信息可能已经沧海桑田

原题链接

思路

这题基本上就是采用迪杰斯特拉算法求最短路径。只是我一开始给想复杂化了,结果走了不少弯路。因为城市的数量最多可以 500 个,是个相当大的数,同时并不是每个城市之间都有路,如果用矩阵存储图的话是个稀疏矩阵,因此我就想用邻接表存。这下可好,光是读入数据到邻接表就写了我 80 行代码,之后的算法更是无从下手了,完全走进死胡同了,无奈之下,只好去网上查人家的做法,发现人家压根就没考虑稀疏矩阵的事,都是用邻接矩阵存,更有甚者直接定义了一个 500*500 的矩阵,空间复杂度高到了极致。我还是折中一下吧,用题目中给的城市总数定义邻接矩阵大小,实现方法还是我们最喜欢的 vector,为此我特意搜索了一下 vector 实现二维数组的方法,应该是这样的 vector<vector<int> > matrix(n, vector<int>(m)),注意两个 > 之间有空格。这道题需要在传统迪杰斯特拉算法上增加的东西是:1. 权值相同的最短路径总数。2. 可以集合到的救援队总数。这些在代码里都有体现,应该一看就能明白。

代码

#include <iostream>
#include <vector>
#include <limits.h>
using namespace std;

int main()
{
	int cityNum, roadNum, depa, dest;
	cin >> cityNum >> roadNum >> depa >> dest;
	vector<vector<int> > adjMatrix(cityNum, vector<int>(cityNum,INT_MAX));	//邻接矩阵
	vector<int> dist(cityNum,INT_MAX);	//记录到各个城市的最短距离
	vector<int> pathSum(cityNum);	//到各个城市的最短路径数量
	vector<int> teamSum(cityNum);	//到各个城市能集合到的救援队数量
	vector<bool> reach(cityNum);	//记录计算过程中是否以最短路径到各个城市
	vector<int> teams(cityNum);	//每个城市救援队数量
	for (int i = 0; i < cityNum; i++)
	{
		adjMatrix[i][i] = 0;
	}
	for (int i = 0; i < cityNum; i++)
	{
		int tnum;
		cin >> tnum;
		teams[i]=tnum;
	}
	for (int i = 0; i < roadNum; i++)
	{
		int from, to, weight;
		cin >> from >> to >> weight;
		adjMatrix[from][to] = weight;
		adjMatrix[to][from] = weight;
		if (from == depa)	//根据出发点的相邻路径更新相关信息
		{
			dist[to] = weight;
			teamSum[to] = teams[from] + teams[to];
			pathSum[to] = 1;
		}
	}
	dist[depa] = 0;
	teamSum[depa] = teams[depa];
	pathSum[depa] = 1;
	reach[depa] = true;
	//注意,只有当reach[i]为true时,dist[i]才是出发点到它真正的最短路径长度,否则就还得更新
	for (int i = 0; i < cityNum; i++)
	{
		int min = INT_MAX, u=-1;
		for (int j = 0; j < cityNum; j++)
		{
			if (reach[j] == false && dist[j] < min)
			{
				min = dist[j];
				u = j;
			}
		}
		if (min == INT_MAX)
			break;
		reach[u] = true;
		for (int j = 0; j < cityNum; j++)
		{
			if (reach[j] == false && adjMatrix[u][j]!=INT_MAX && dist[u] + adjMatrix[u][j] < dist[j])	
				//若adjMatrix[u][j]==INT_MAX就直接排除掉,如果没有判断,那么当它为INT_MAX的时候,dist[u]+adjMatrix[u][j]会反转变成负数,这样反而会符合<dist[j]的要求
			{
				dist[j] = dist[u] + adjMatrix[u][j];	//更新最短距离
				pathSum[j] = pathSum[u];	//更新可达的最短路径总数
				teamSum[j] = teamSum[u] + teams[j];		//更新可以集合到的救援队总数
			}
			else if (reach[j] == false && (dist[u] + adjMatrix[u][j]) == dist[j])	//如果有另一条最短距离相同的路径
			{
				pathSum[j] += pathSum[u];	//增加可达的最短路径总数
				if (teamSum[j] < teamSum[u] + teams[j])	//如果这条路径可以集合到的救援队数量更多
					teamSum[j] = teamSum[u] + teams[j];	//更新可以集合到的救援队数量
			}
		}
	}
	cout << pathSum[dest] << ' ' << teamSum[dest];
}

感想和注意事项

  1. 当节省空间的方法过于复杂时,尽量用空间换时间,而不是想着怎么去节省空间。比如说图用邻接矩阵而不是邻接表存,这样读入数据以及之后的运算过程都会快很多。

  2. 代码中用到了一个 INT_MAX 值,表示不可达的路径长度,这个常量包含在 limits.h 头文件中。但是需要注意的是,如果把 INT_MAX 的值加上一个正数以后,会反转变成负数,从而导致程序出现错误。因此我在代码中进行了判断。我看网上有些方法就是自己定义一个常量 INF,再给它手工赋一个很大但又不超过 int 表示范围的数比如 1111111,从而来表示不可达的路径长度,这样也行,但我个人感觉不太严谨。

  3. vector 真是神器,太好用了。当然你也可以直接定义一个静态数组,用一个很大的数表示它的容量,事实上网上大部分解答都是这么做的,但我不用这种方法也是和上面一样的原因,这种做法太不严谨而且太浪费空间了。当能够用很低的代价(多几行代码)节省大量的空间时,这种做法还是值得的。

  • PAT
    25 引用 • 1 回帖 • 1 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...