我的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++解题报告: