PAT-ADVANCED1087——All Roads Lead to Rome

版权声明:我的GitHub:https://github.com/617076674。真诚求星! https://blog.csdn.net/qq_41231926/article/details/83541465

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805379664297984

题目描述:

题目翻译:

1087 条条大路通罗马

从你的城市到罗马的确存在许多不同的旅行路线。你需要为你的客户挑选一条花费最少但是快乐感最多的路线。

输入格式:

每个输入文件包含一个测试用例。在每个测试用例中,第一行包含两个正整数N(2 <= N <= 200),代表城市的数量,以及K,代表城市间的道路总数,然后紧跟着的是起始城市。接下来的N - 1行提供了城市的名字和能从该城市获得的快乐感,不包括起始城市。接下来的K行,每一行以下述形式:City1 City2 Cost提供两个城市间的一条道路信息。城市的名字是一个由3个大写的英文字母组成的字符串,目的地始终是代表罗马的ROM。

输出格式:

对每个测试用例,你需要找出花费最少的路线。如果这样的路线不唯一,你就需要找出获得快乐感最多的路线。如果这样的路线还不唯一,你需要输出平均快乐感最多的路线——题目保证这样的路线一定存在且唯一。

在输出的第一行,你需要输出4个数字:最少花费的路径数量,你推荐的路径的花费、快乐感、平均快乐感(只取整数部分)。在接下来的一行,你需要按下述格式输出路线:City1->City2->...->ROM。

输入样例:

6 7 HZH
ROM 100
PKN 40
GDN 55
PRS 95
BLN 80
ROM GDN 1
BLN ROM 1
HZH PKN 1
PRS ROM 2
BLN HZH 2
PKN GDN 1
HZH PRS 1

输出样例:

3 3 195 97
HZH->PRS->ROM

知识点:Dijkstra算法、Bellman-Ford算法、SPFA算法、深度优先遍历

思路一:Dijkstra算法+深度优先遍历(邻接表实现)

时间复杂度是O(N ^ 2)。空间复杂度是O(N + K)。

C++代码:

#include<iostream>
#include<vector>
#include<map>
#include<string>

using namespace std;

struct node {
	int v;	//节点编号
	int cost;	//边权值
	node(int _v, int _cost) : v(_v), cost(_cost) {}	//构造函数
};

int N;	//城市数量
int K;	//道路条数
int INF = 1000000000;	//无穷大数 
string start;	//起始点城市名字
int happy[201];	//存放各个城市的快乐值
map<string, int> stringToInt;	//城市名字->编号
map<int, string> intToString; 	//编号->城市名字
vector<node> graph[201];	//无向图
int d[201];	//记录从起始城市到达点i的最少花费值 
bool visited[201];	//标记数组
vector<int> pre[201];	//记录前一个节点 
vector<int> path;
vector<int> tempPath;
int optHappy = 0;
double optAverageHappy = 0.0;
int count = 0;	//记录最短路径条数 

void dijkstra(int s);
void dfs(int nowVisit);

int main() {
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i <= N - 1; i++) {
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++) {
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1].push_back(node(id2, cost));
		graph[id2].push_back(node(id1, cost));
	}
	int destination = stringToInt["ROM"];
	dijkstra(0);
	dfs(destination);
	cout << count << " " << d[destination] << " " << optHappy << " " << (int)optAverageHappy << endl;
	for(int i = path.size() - 1; i >= 0; i--){
		cout << intToString[path[i]];
		if(i != 0){
			cout << "->";
		}
	}
	return 0;
}

void dijkstra(int s){
	for(int i = 0; i < N; i++){
		d[i] = INF;
	} 
	d[s] = 0;
	for(int i = 0; i < N; i++){
		int u = -1, min = INF;
		for(int j = 0; j < N; j++){
			if(!visited[j] && d[j] < min){
				min = d[j];
				u = j;
			}
		}
		if(u == -1){
			return;
		}
		visited[u] = true;
		for(int j = 0; j < graph[u].size(); j++){
			int v = graph[u][j].v;
			int cost = graph[u][j].cost;
			if(!visited[v]){
				if(d[u] + cost < d[v]){
					d[v] = d[u] + cost;
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u] + cost == d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
} 

void dfs(int nowVisit){
	if(nowVisit == 0){
		count++;
		tempPath.push_back(nowVisit);
		int happyValue = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--){
			happyValue += happy[tempPath[i]];
		}
		double averageHappyValue = 1.0 * happyValue / (tempPath.size() - 1);
		if(happyValue > optHappy){
			optHappy = happyValue;
			optAverageHappy = averageHappyValue;	//也需要更新optAverageHappy的值 
			path = tempPath;
		}else if(happyValue == optHappy && averageHappyValue > optAverageHappy){
			optAverageHappy = averageHappyValue;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	} 
	tempPath.push_back(nowVisit);
	for(int i = 0; i < pre[nowVisit].size(); i++){
		dfs(pre[nowVisit][i]);
	}
	tempPath.pop_back();
} 

C++解题报告:

思路二:Bellman-Ford算法+深度优先遍历(邻接表实现)

时间复杂度是O(K * N)。空间复杂度是O(N + K)。

C++代码:

#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<set>

using namespace std;

struct node {
	int v;	//节点编号
	int cost;	//边权值
	node(int _v, int _cost) : v(_v), cost(_cost) {}	//构造函数
};

int N;	//城市数量
int K;	//道路条数
int INF = 1000000000;	//无穷大数
string start;	//起始点城市名字
int happy[201];	//存放各个城市的快乐值
map<string, int> stringToInt;	//城市名字->编号
map<int, string> intToString; 	//编号->城市名字
vector<node> graph[201];	//无向图
int d[201];	//记录从起始城市到达点i的最少花费值
set<int> pre[201];	//记录前一个节点
vector<int> path;
vector<int> tempPath;
int optHappy = 0;
double optAverageHappy = 0.0;
int count = 0;	//记录最短路径条数

bool bellmanFord(int s);
void dfs(int nowVisit);

int main() {
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i <= N - 1; i++) {
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++) {
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1].push_back(node(id2, cost));
		graph[id2].push_back(node(id1, cost));
	}
	int destination = stringToInt["ROM"];
	bellmanFord(0);
	dfs(destination);
	cout << count << " " << d[destination] << " " << optHappy << " " << (int)optAverageHappy << endl;
	for(int i = path.size() - 1; i >= 0; i--) {
		cout << intToString[path[i]];
		if(i != 0) {
			cout << "->";
		}
	}
	return 0;
}

bool bellmanFord(int s) {
	for(int i = 0; i < N; i++) {
		d[i] = INF;
	}
	d[s] = 0;
	for(int i = 0; i < N - 1; i++) {
		for(int u = 0; u < N; u++) {
			for(int j = 0; j < graph[u].size(); j++) {
				int v = graph[u][j].v;
				int cost = graph[u][j].cost;
				if(cost + d[u] < d[v]) {
					d[v] = cost + d[u];
					pre[v].clear();
					pre[v].insert(u);
				} else if(cost + d[u] == d[v]) {
					pre[v].insert(u);
				}
			}
		}
	}
	for(int u = 0; u < N; u++) {
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int cost = graph[u][j].cost;
			if(cost + d[u] < d[v]) {
				return false;
			}
		}
	}
	return true;
}

void dfs(int nowVisit) {
	if(nowVisit == 0) {
		count++;
		tempPath.push_back(nowVisit);
		int happyValue = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--) {
			happyValue += happy[tempPath[i]];
		}
		double averageHappyValue = 1.0 * happyValue / (tempPath.size() - 1);
		if(happyValue > optHappy) {
			optHappy = happyValue;
			optAverageHappy = averageHappyValue;
			path = tempPath;
		} else if(happyValue == optHappy && averageHappyValue > optAverageHappy) {
			optAverageHappy = averageHappyValue;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(nowVisit);
	set<int>::iterator it;
	for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
		dfs(*it);
	}
	tempPath.pop_back();
}

C++解题报告:

思路三:SPFA算法+深度优先遍历(邻接表实现)

期望时间复杂度是O(kK),其中K是图的边数,k是一个常数,在很多情况下k不超过2,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。空间复杂度是O(N + K)。

C++代码:

#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<set>
#include<queue>

using namespace std;

struct node {
	int v;	//节点编号
	int cost;	//边权值
	node(int _v, int _cost) : v(_v), cost(_cost) {}	//构造函数
};

int N;	//城市数量
int K;	//道路条数
int INF = 1000000000;	//无穷大数
string start;	//起始点城市名字
int happy[201];	//存放各个城市的快乐值
map<string, int> stringToInt;	//城市名字->编号
map<int, string> intToString; 	//编号->城市名字
vector<node> graph[201];	//无向图
int d[201];	//记录从起始城市到达点i的最少花费值
set<int> pre[201];	//记录前一个节点
vector<int> path;
vector<int> tempPath;
int optHappy = 0;
double optAverageHappy = 0.0;
int count = 0;	//记录最短路径条数
bool inq[201] = {false};	//记录节点是否在队列中
int countInq[201] = {0};	//统计每个节点的入队次数 

bool spfa(int s);
void dfs(int nowVisit);

int main() {
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i <= N - 1; i++) {
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++) {
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1].push_back(node(id2, cost));
		graph[id2].push_back(node(id1, cost));
	}
	int destination = stringToInt["ROM"];
	spfa(0);
	dfs(destination);
	cout << count << " " << d[destination] << " " << optHappy << " " << (int)optAverageHappy << endl;
	for(int i = path.size() - 1; i >= 0; i--) {
		cout << intToString[path[i]];
		if(i != 0) {
			cout << "->";
		}
	}
	return 0;
}

bool spfa(int s) {
	for(int i = 0; i < N; i++){
		d[i] = INF;
	}
	d[s] = 0;
	queue<int> q;
	q.push(s);
	inq[s] = true;
	countInq[s]++;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int j = 0; j < graph[u].size(); j++){
			int v = graph[u][j].v;
			int cost = graph[u][j].cost;
			if(d[u] + cost < d[v]){
				d[v] = d[u] + cost;
				pre[v].clear();
				pre[v].insert(u);
				if(!inq[v]){
					q.push(v);
					inq[v] = true;
					countInq[v]++;
					if(countInq[v] > N){
						return false;
					}
				}
			}else if(d[u] + cost == d[v]){
				pre[v].insert(u);
				if(!inq[v]){
					q.push(v);
					inq[v] = true;
					countInq[v]++;
					if(countInq[v] > N){
						return false;
					}
				}
			}
		}
	}
	return true;	
}

void dfs(int nowVisit) {
	if(nowVisit == 0) {
		count++;
		tempPath.push_back(nowVisit);
		int happyValue = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--) {
			happyValue += happy[tempPath[i]];
		}
		double averageHappyValue = 1.0 * happyValue / (tempPath.size() - 1);
		if(happyValue > optHappy) {
			optHappy = happyValue;
			optAverageHappy = averageHappyValue;
			path = tempPath;
		} else if(happyValue == optHappy && averageHappyValue > optAverageHappy) {
			optAverageHappy = averageHappyValue;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	tempPath.push_back(nowVisit);
	set<int>::iterator it;
	for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
		dfs(*it);
	}
	tempPath.pop_back();
}

C++解题报告:

思路四:Dijkstra算法+深度优先遍历(邻接矩阵实现)

时间复杂度和空间复杂度均是O(N ^ 2)。

C++代码:

#include<iostream>
#include<map>
#include<vector>
#include<string>

using namespace std;

int N;	//城市数量 
int K;	//道路条数 
int INF = 1000000000;
string start;	//起始城市
int graph[201][201];
map<string, int> stringToInt;	//城市名称->城市编号
map<int, string> intToString;	//城市编号->城市名称
int happy[201];	//每个城市的快乐数
int d[201];
bool visited[201] = {false};
vector<int> pre[201];
vector<int> tempPath;
vector<int> path;
int optValue1 = 0;
double optValue2 = 0;
int count = 0; 

void dijkstra(int s);
void dfs(int nowVisit);

int main(){
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0 
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i < N; i++){
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	for(int i = 0; i < N; i++){
		for(int j = 0; j < N; j++){
			graph[i][j] = graph[j][i] = INF;
		}
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++){
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1][id2] = graph[id2][id1] = cost;
	}
	dijkstra(0);
	int destination = stringToInt["ROM"];
	dfs(destination); 
	cout << count << " " << d[destination] << " " << optValue1 << " " << (int)optValue2 << endl;
	for(int i = path.size() - 1; i >= 0; i--){
		cout << intToString[path[i]];
		if(i != 0){
			cout << "->";
		}
	} 
	cout << endl;
	return 0;
}

void dijkstra(int s){
	for(int i = 0; i < N; i++){
		d[i] = INF;
	}
	d[s] = 0;
	for(int i = 0; i < N; i++){
		int u = -1, min = INF;
		for(int j = 0; j < N; j++){
			if(!visited[j] && d[j] < min){
				min = d[j];
				u = j;
			}
		}
		if(u == -1){
			return;
		}
		visited[u] = true;
		for(int v = 0; v < N; v++){
			if(!visited[v] && graph[u][v] != INF){
				if(d[u] + graph[u][v] < d[v]){
					d[v] = d[u] + graph[u][v];
					pre[v].clear();
					pre[v].push_back(u);
				}else if(d[u] + graph[u][v] == d[v]){
					pre[v].push_back(u);
				}
			}
		}
	}
}

void dfs(int nowVisit){
	tempPath.push_back(nowVisit);
	if(nowVisit == 0){
		count++;
		int value1 = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--){
			value1 += happy[tempPath[i]];
		}
		double value2 = value1 * 1.0 / (tempPath.size() - 1);
		if(value1 > optValue1){
			optValue1 = value1;
			optValue2 = value2;
			path = tempPath;
		}else if(value1 == optValue1 && value2 > optValue2){
			optValue2 = value2;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	for(int i = 0; i < pre[nowVisit].size(); i++){
		dfs(pre[nowVisit][i]);
	}
	tempPath.pop_back();
}

C++解题报告:

思路五:Bellman-Ford算法+深度优先遍历(邻接矩阵实现)

时间复杂度是O(N ^ 3)。空间复杂度是O(N ^ 2)。

C++代码:

#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<set>

using namespace std;

int N;	//城市数量
int K;	//道路条数
int INF = 1000000000;
string start;	//起始城市
int graph[201][201];
map<string, int> stringToInt;	//城市名称->城市编号
map<int, string> intToString;	//城市编号->城市名称
int happy[201];	//每个城市的快乐数
int d[201];
set<int> pre[201];
vector<int> tempPath;
vector<int> path;
int optValue1 = 0;
double optValue2 = 0;
int count = 0;

bool bellmanFord(int s);
void dfs(int nowVisit);

int main() {
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i < N; i++) {
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			graph[i][j] = graph[j][i] = INF;
		}
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++) {
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1][id2] = graph[id2][id1] = cost;
	}
	bellmanFord(0);
	int destination = stringToInt["ROM"];
	dfs(destination);
	cout << count << " " << d[destination] << " " << optValue1 << " " << (int)optValue2 << endl;
	for(int i = path.size() - 1; i >= 0; i--) {
		cout << intToString[path[i]];
		if(i != 0) {
			cout << "->";
		}
	}
	cout << endl;
	return 0;
}

bool bellmanFord(int s) {
	for(int i = 0; i < N; i++) {
		d[i] = INF;
	}
	d[s] = 0;
	for(int i = 0; i < N - 1; i++) {
		for(int u = 0; u < N; u++) {
			for(int v = 0; v < N; v++) {
				if(graph[u][v] != INF) {
					if(d[u] + graph[u][v] < d[v]) {
						d[v] = d[u] + graph[u][v];
						pre[v].clear();
						pre[v].insert(u);
					} else if(d[u] + graph[u][v] == d[v]) {
						pre[v].insert(u);
					}
				}
			}
		}
	}
	for(int u = 0; u < N; u++) {
		for(int v = 0; v < N; v++) {
			if(graph[u][v] != INF) {
				if(d[u] + graph[u][v] < d[v]) {
					return false;
				}
			}
		}
	}
	return true;
}

void dfs(int nowVisit) {
	tempPath.push_back(nowVisit);
	if(nowVisit == 0) {
		count++;
		int value1 = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--) {
			value1 += happy[tempPath[i]];
		}
		double value2 = value1 * 1.0 / (tempPath.size() - 1);
		if(value1 > optValue1) {
			optValue1 = value1;
			optValue2 = value2;
			path = tempPath;
		} else if(value1 == optValue1 && value2 > optValue2) {
			optValue2 = value2;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
		dfs(*it);
	}
	tempPath.pop_back();
}

C++解题报告:

思路六:SPFA算法+深度优先遍历(邻接矩阵实现)

期望时间复杂度是O(kN),其中k是一个常数,在很多情况下k不超过2,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。空间复杂度是O(N ^ 2)。

C++代码:

#include<iostream>
#include<map>
#include<vector>
#include<string>
#include<set>
#include<queue>

using namespace std;

int N;	//城市数量
int K;	//道路条数
int INF = 1000000000;
string start;	//起始城市
int graph[201][201];
map<string, int> stringToInt;	//城市名称->城市编号
map<int, string> intToString;	//城市编号->城市名称
int happy[201];	//每个城市的快乐数
int d[201];
set<int> pre[201];
vector<int> tempPath;
vector<int> path;
int optValue1 = 0;
double optValue2 = 0;
int count = 0;
bool inq[201] = {false};
int countInq[201] = {0};

bool spfa(int s);
void dfs(int nowVisit);

int main() {
	cin >> N >> K >> start;
	stringToInt[start] = 0;	//起始城市编号为0
	intToString[0] = start;
	string city;
	int happyness;
	for(int i = 1; i < N; i++) {
		cin >> city >> happyness;
		stringToInt[city] = i;
		intToString[i] = city;
		happy[i] = happyness;
	}
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			graph[i][j] = graph[j][i] = INF;
		}
	}
	string city1, city2;
	int cost;
	for(int i = 0; i < K; i++) {
		cin >> city1 >> city2 >> cost;
		int id1 = stringToInt[city1];
		int id2 = stringToInt[city2];
		graph[id1][id2] = graph[id2][id1] = cost;
	}
	spfa(0);
	int destination = stringToInt["ROM"];
	dfs(destination);
	cout << count << " " << d[destination] << " " << optValue1 << " " << (int)optValue2 << endl;
	for(int i = path.size() - 1; i >= 0; i--) {
		cout << intToString[path[i]];
		if(i != 0) {
			cout << "->";
		}
	}
	cout << endl;
	return 0;
}

bool spfa(int s) {
	for(int i = 0; i < N; i++) {
		d[i] = INF;
	}
	d[s] = 0;
	queue<int> q;
	q.push(s);
	inq[s] = true;
	countInq[s]++;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		for(int v = 0; v < N; v++){
			if(graph[u][v] != INF){
				if(d[u] + graph[u][v] < d[v]){
					d[v] = d[u] + graph[u][v];
					pre[v].clear();
					pre[v].insert(u);
					if(!inq[v]){
						q.push(v);
						inq[v] = true;
						countInq[v]++;
						if(countInq[v] >= N){
							return false;
						}
					}
				}else if(d[u] + graph[u][v] == d[v]){
					pre[v].insert(u);
					if(!inq[v]){
						q.push(v);
						inq[v] = true;
						countInq[v]++;
						if(countInq[v] >= N){
							return false;
						}
					}
				}
			}
		}
	}
	return true;
}

void dfs(int nowVisit) {
	tempPath.push_back(nowVisit);
	if(nowVisit == 0) {
		count++;
		int value1 = 0;
		for(int i = tempPath.size() - 2; i >= 0; i--) {
			value1 += happy[tempPath[i]];
		}
		double value2 = value1 * 1.0 / (tempPath.size() - 1);
		if(value1 > optValue1) {
			optValue1 = value1;
			optValue2 = value2;
			path = tempPath;
		} else if(value1 == optValue1 && value2 > optValue2) {
			optValue2 = value2;
			path = tempPath;
		}
		tempPath.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
		dfs(*it);
	}
	tempPath.pop_back();
}

C++解题报告:

猜你喜欢

转载自blog.csdn.net/qq_41231926/article/details/83541465