记PAT A1003题解题过程

在这里插入图片描述

这是题目,给各位翻译一下。现有一个城市群。每个城市都有自己的救援力量,每个城市之间的路途距离也有给出。现在你作为区域的救援队长,一旦出现紧急情况。你需要在最短的距离赶到救援现场,并尽可能从途中带走更多的救援力量到达救灾点。输入的第一行分别是:城市数(顶点数),城市之间的道路数(边数),救援队长出发点,事故发生点,第二行是每个城市的救援力量,接下来的边数行是边权值信息。每行对应着起点,终点,以及路途距离。输出是最短路径的数目和可以携带到的最大救援力量。
(我的解释喜欢写在代码里。算作注释。)

#include <iostream>
using namespace std;

const int MAXV = 1000;
const int INF = 1000000000;
int g[MAXV][MAXV];  //图的边值。未初始化的全局变量会自动初始化为0
bool vit[MAXV] = { false };   //判断是否已经访问过。
int d[MAXV];   //记录最短距离。
//这是常规的三个数组

int num[MAXV];   //记录到达这个顶点的最短路径的数量,默认初始化起点的值为1,其余为0
int weight[MAXV];    //记录该顶点的救援力量。主函数一开始就会由输入初始化。
int w[MAXV];   //记录到达该顶点的最大救援力量。

void Dijkstra(int n) {
	for (int i = 0; i < n; i++) {
		int u = -1; int MIN = INF;
		for (int j = 0; j < n; j++) {
			if (vit[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vit[u] = true;
		for (int v = 0; v < n; v++) {
			if (vit[v] == false && g[u][v] != 0) {
				if (d[u] + g[u][v] < d[v]) {
					d[v] = d[u] + g[u][v];
					//最小距离改进。还需要维护num数组和w数组。
					num[v] = num[u];   //如果走u过,距离可以更短,那么v的数量就和u的数量相等。
					w[v] = w[u] + weight[v];   //如果距离更近,直接选择这条路,不考虑稍微远的那条路是否有更大的救援力量。
					                           //因为题意要求的就是尽快到达,之后再有尽可能多的救援力量。
				}
				else if(d[u]+g[u][v]==d[v]){
					num[v] += num[u];  //如果一样远,那么v的数量就需要加上u的数量。
					if (w[u] + weight[v] > w[v]) {
						w[v] = w[u] + weight[v];
					}
				}
			}
		}
	}
}

int main() {
	int N, M;
	int C1, C2;
	fill(d, d + MAXV, INF);
	cin >> N >> M >> C1 >> C2;
	num[C1] = 1;
	d[C1] = 0;
	for (int i = 0; i < N; i++) {
		cin>>weight[i];
	}
	w[C1] = weight[C1];
	for (int i = 0; i < M; i++) {
		int c, d1, e;
		cin >> c >> d1 >> e;
		g[c][d1] = g[d1][c] = e;
	}
	Dijkstra(N);
	cout << num[C2] << " "<<w[C2];
	return 0;
}

算法笔记还给出了更加模板化的写法。就是先用Dijkstra算法记录所有的最短路径(只考虑距离),然后再从这些路径里面选择出满足第二尺度的最优的路径。(第二尺度就是指点权啊,比如这里的救援力量,边权啊,比如走一条边的花费等等。)这里给出的方法是用DFS算法去找出满足第二尺度最优的路线。

#include <iostream>
#include <vector>
using namespace std;

const int MAXV = 1000;
const int INF = 1000000000;
int g[MAXV][MAXV];  //图的边值。
bool vit[MAXV] = { false };   //判断是否已经访问过。
int d[MAXV];   //记录最短距离。
//这是常规的三个数组

int num[MAXV];   //记录到达这个顶点的最短路径的数量,默认初始化起点的值为1,其余为0
int weight[MAXV];    //记录该顶点的救援力量。主函数一开始就会由输入初始化。
vector<int> pre[MAXV];   //这是一个数组,每个数组元素都是一个数组。
int optvalue=0;
vector<int> path, temppath;  //记录最短的那个路径以及中间临时的路径。

void Dijkstra(int n) {
	for (int i = 0; i < n; i++) {
		int u = -1; int MIN = INF;
		for (int j = 0; j < n; j++) {
			if (vit[j] == false && d[j] < MIN) {
				u = j;
				MIN = d[j];
			}
		}
		if (u == -1) return;
		vit[u] = true;
		for (int v = 0; v < n; v++) {
			if (vit[v] == false && g[u][v] != 0) {
				if (d[u] + g[u][v] < d[v]) {
					d[v] = d[u] + g[u][v];
					//最小距离改进。
					pre[v].clear();
					pre[v].push_back(u);
					num[v] = num[u];   //如果走u过,距离可以更短,那么v的数量就和u的数量相等。
				}
				else if (d[u] + g[u][v] == d[v]) {
					pre[v].push_back(u);
					num[v] += num[u];  //如果一样远,那么v的数量就需要加上u的数量。
				}
			}
		}
	}
}
//重点是这里加的这个DFS1函数,这里来处理第二尺度。意思就是说把两个尺度的处理分开,使之更加的清晰可控,逻辑没那么复杂。
void DFS1(int C1,int C2) {
	//先处理边界
	if (C2 == C1) {
		temppath.push_back(C2);
		int value = 0;
		for (int i = 0; i < temppath.size(); i++) {
			value += weight[temppath[i]];
		}
		if (value > optvalue) {
			optvalue = value;
			path = temppath;
		}
		temppath.pop_back();
		return;
	}
	temppath.push_back(C2);
	for (int i = 0; i < pre[C2].size(); i++) {
		DFS1(C1, pre[C2][i]);
	}
	temppath.pop_back();
}

int main() {
	int N, M;
	int C1, C2;   //起点和终点
	fill(d, d + MAXV, INF);
	cin >> N >> M >> C1 >> C2;
	num[C1] = 1;
	d[C1] = 0;
	for (int i = 0; i < N; i++) {
		cin >> weight[i];
	}
	for (int i = 0; i < M; i++) {
		int c, d1, e;
		cin >> c >> d1 >> e;
		g[c][d1] = g[d1][c] = e;
	}
	Dijkstra(N);
	DFS1(C1, C2);
	cout << num[C2] << " "<<optvalue;
	return 0;
}

好了,以上就是这道题的代码以及思想。谢谢阅读!

原创文章 77 获赞 4 访问量 9053

猜你喜欢

转载自blog.csdn.net/weixin_40007143/article/details/104672378