PAT-ADVANCED1111——Online Map

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

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

题目描述:

PAT-ADVANCED1111——Online Map

题目翻译:

1111 在线地图

输入你所在的位置和目的地,一张在线地图会推荐几条路线。现在你的任务是给用户推荐两条路线:第一条是最短的,第二条是最快的。题目保证对任何请求一定存在一条路径。

输入格式:

每个输入文件包含一个测试用例。在每个测试用例中,第一行有2个正整数:N(2 <= N <= 500)和M,分别表示地图上十字路口的数量和街道的数量。接下来的M行,每一行按下述格式描述一条街道:

V1 V2 one-way length time

V1和V2是街道两个端点的标记,范围在0 ~ N - 1之间。如果one-way是1,说明这条街道是单向的,只能从V1到V2,如果是0则是双向的。length是这条街道的长度。time是通过这条街道所花费的时间。

最后提供了起点和终点的信息。

输出格式:

对每个测试用例,第一行按以下格式输出从起点到终点的最短路径信息,其中D表示最短距离:

Distance = D: source -> v1 -> ... -> destination

接下来的一行按以下格式输出从起店到终点的最快路径信息,其中T表示最短时间:

Time = T: source -> w1 -> ... -> destination

如果最短路径不唯一,输出最短路径中最快的那条路径,题目保证这样的路径唯一。如果最快路径不唯一,输出经过最少十字路口的路径,题目保证这样的路径唯一。

如果最短路径和最快路径相同,按以下格式在一行中输出信息:

Distance = D; Time = T: source -> u1 -> ... -> destination

输入样例1:

10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
3 4 0 3 2
3 9 1 4 1
0 6 0 1 1
7 5 1 2 1
8 5 1 2 1
2 3 0 2 2
2 1 1 1 1
1 3 0 3 1
1 4 0 1 1
9 7 1 3 1
5 1 0 5 2
6 5 1 1 2
3 5

输出样例1:

Distance = 6: 3 -> 4 -> 8 -> 5
Time = 3: 3 -> 1 -> 5

输入样例2:

7 9
0 4 1 1 1
1 6 1 1 3
2 6 1 1 1
2 5 1 2 2
3 0 0 1 1
3 1 1 1 3
3 2 1 1 2
4 5 0 2 2
6 5 1 1 2
3 5

输出样例2:

Distance = 3; Time = 4: 3 -> 2 -> 5

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

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

分别以长度和时间为边权求两次Dijkstra算法+深度优先遍历即可。

注意点:

对于邻接表来说,要求AB的边权,我们需要遍历与A相连的所有端点才能找到B端点,这一方面邻接表不如邻接矩阵直接。

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

C++代码:

#include<iostream>
#include<vector>

using namespace std;

struct node {
	int v;	//端点编号
	int length;	//道路长度
	int time;	//道路时间
	node(int _v, int _length, int _time) : v(_v), length(_length), time(_time) {};	//构造函数
};

int N;	//十字路口数量
int M;	//道路数量
int source;
int destination;
int INF = 1000000000;
vector<node> graph[501];	//有向图
int d1[501];
int d2[501];
bool visited1[501] = {false};
bool visited2[501] = {false};
vector<int> pre1[501];
vector<int> pre2[501];
vector<int> tempPath1;
vector<int> tempPath2;
vector<int> path1;
vector<int> path2;
int optValue1 = INF;
int optValue2 = INF;

void dijkstra1(int s);
void dfs1(int nowVisit);
void dijkstra2(int s);
void dfs2(int nowVisit);

int main() {
	cin >> N >> M;
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		graph[V1].push_back(node(V2, length, time));
		if(one_way == 0) {
			graph[V2].push_back(node(V1, length, time));
		}
	}
	cin >> source >> destination;
	dijkstra1(source);
	dfs1(destination);
	dijkstra2(source);
	dfs2(destination);
	bool flag = true;
	if(path1.size() != path2.size()) {
		flag = false;
	} else {
		for(int i = 0; i < path1.size(); i++) {
			if(path1[i] != path2[i]) {
				flag = false;
				break;
			}
		}
	}
	if(flag) {
		cout << "Distance = " << d1[destination] << "; Time = " << d2[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	} else {
		cout <<"Distance = " << d1[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
		cout <<"Time = " << d2[destination] << ": ";
		for(int i = path2.size() - 1; i >= 0; i--) {
			cout << path2[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	}
	return 0;
}

void dijkstra1(int s) {
	for(int i = 0; i < N; i++) {
		d1[i] = INF;
	}
	d1[s] = 0;
	for(int i = 0; i < N; i++) {
		int u = -1, min = INF;
		for(int j = 0; j < N; j++) {
			if(!visited1[j] && d1[j] < min) {
				min = d1[j];
				u = j;
			}
		}
		if(u == -1) {
			return;
		}
		visited1[u] = true;
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int length = graph[u][j].length;
			if(!visited1[v]) {
				if(d1[u] + length < d1[v]) {
					d1[v] = d1[u] + length;
					pre1[v].clear();
					pre1[v].push_back(u);
				} else if(d1[u] + length == d1[v]) {
					pre1[v].push_back(u);
				}
			}
		}
	}
}

void dfs1(int nowVisit) {
	tempPath1.push_back(nowVisit);
	if(nowVisit == source) {
		int value1 = 0;
		for(int i = tempPath1.size() - 1; i > 0; i--) {
			//对于邻接表求边权,需要遍历该点连接的所有点才能寻找到所要求的那条边的边权
			for(int j = 0; j < graph[tempPath1[i]].size(); j++){	
				if(graph[tempPath1[i]][j].v == tempPath1[i - 1]){
					value1 += graph[tempPath1[i]][j].time;
					break; 
				}
			}
		}
		if(value1 < optValue1) {
			optValue1 = value1;
			path1 = tempPath1;
		}
		tempPath1.pop_back();
		return;
	}
	for(int i = 0; i < pre1[nowVisit].size(); i++) {
		dfs1(pre1[nowVisit][i]);
	}
	tempPath1.pop_back();
}

void dijkstra2(int s) {
	for(int i = 0; i < N; i++) {
		d2[i] = INF;
	}
	d2[s] = 0;
	for(int i = 0; i < N; i++) {
		int u = -1, min = INF;
		for(int j = 0; j < N; j++) {
			if(!visited2[j] && d2[j] < min) {
				min = d2[j];
				u = j;
			}
		}
		if(u == -1) {
			return;
		}
		visited2[u] = true;
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int time = graph[u][j].time;
			if(!visited2[v]) {
				if(d2[u] + time < d2[v]) {
					d2[v] = d2[u] + time;
					pre2[v].clear();
					pre2[v].push_back(u);
				} else if(d2[u] + time == d2[v]) {
					pre2[v].push_back(u);
				}
			}
		}
	}
}

void dfs2(int nowVisit) {
	tempPath2.push_back(nowVisit);
	if(nowVisit == source) {
		int value2 = tempPath2.size();
		if(value2 < optValue2) {
			optValue2 = value2;
			path2 = tempPath2;
		}
		tempPath2.pop_back();
		return;
	}
	for(int i = 0; i < pre2[nowVisit].size(); i++) {
		dfs2(pre2[nowVisit][i]);
	}
	tempPath2.pop_back();
}

C++解题报告:

PAT-ADVANCED1111——Online Map

思路二:Bellman-Ford算法+深度优先遍历(邻接表实现)(测试点4会超时)

分别以长度和时间为边权求两次Bellman-Ford算法+深度优先遍历即可。

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

C++代码:

#include<iostream>
#include<vector>
#include<set>

using namespace std;

struct node {
	int v;	//端点编号
	int length;	//道路长度
	int time;	//道路时间
	node(int _v, int _length, int _time) : v(_v), length(_length), time(_time) {};	//构造函数
};

int N;	//十字路口数量
int M;	//道路数量
int source;
int destination;
int INF = 1000000000;
vector<node> graph[501];	//有向图
int d1[501];
int d2[501];
set<int> pre1[501];
set<int> pre2[501];
vector<int> tempPath1;
vector<int> tempPath2;
vector<int> path1;
vector<int> path2;
int optValue1 = INF;
int optValue2 = INF;

bool bellmanFord1(int s);
void dfs1(int nowVisit);
bool bellmanFord2(int s);
void dfs2(int nowVisit);

int main() {
	cin >> N >> M;
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		graph[V1].push_back(node(V2, length, time));
		if(one_way == 0) {
			graph[V2].push_back(node(V1, length, time));
		}
	}
	cin >> source >> destination;
	bellmanFord1(source);
	dfs1(destination);
	bellmanFord2(source);
	dfs2(destination);
	bool flag = true;
	if(path1.size() != path2.size()) {
		flag = false;
	} else {
		for(int i = 0; i < path1.size(); i++) {
			if(path1[i] != path2[i]) {
				flag = false;
				break;
			}
		}
	}
	if(flag) {
		cout << "Distance = " << d1[destination] << "; Time = " << d2[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	} else {
		cout <<"Distance = " << d1[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
		cout <<"Time = " << d2[destination] << ": ";
		for(int i = path2.size() - 1; i >= 0; i--) {
			cout << path2[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	}
	return 0;
}

bool bellmanFord1(int s) {
	for(int i = 0; i < N; i++) {
		d1[i] = INF;
	}
	d1[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 length = graph[u][j].length;
				if(d1[u] + length < d1[v]) {
					d1[v] = d1[u] + length;
					pre1[v].clear();
					pre1[v].insert(u);
				} else if(d1[u] + length == d1[v]) {
					pre1[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 length = graph[u][j].length;
			if(d1[u] + length < d1[v]) {
				return false;
			}
		}
	}
	return true;
}

void dfs1(int nowVisit) {
	tempPath1.push_back(nowVisit);
	if(nowVisit == source) {
		int value1 = 0;
		for(int i = tempPath1.size() - 1; i > 0; i--) {
			//对于邻接表求边权,需要遍历该点连接的所有点才能寻找到所要求的那条边的边权
			for(int j = 0; j < graph[tempPath1[i]].size(); j++) {
				if(graph[tempPath1[i]][j].v == tempPath1[i - 1]) {
					value1 += graph[tempPath1[i]][j].time;
					break;
				}
			}
		}
		if(value1 < optValue1) {
			optValue1 = value1;
			path1 = tempPath1;
		}
		tempPath1.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre1[nowVisit].begin(); it != pre1[nowVisit].end(); it++) {
		dfs1(*it);
	}
	tempPath1.pop_back();
}

bool bellmanFord2(int s) {
	for(int i = 0; i < N; i++) {
		d2[i] = INF;
	}
	d2[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 time = graph[u][j].time;
				if(d2[u] + time < d2[v]) {
					d2[v] = d2[u] + time;
					pre2[v].clear();
					pre2[v].insert(u);
				} else if(d2[u] + time == d2[v]) {
					pre2[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 time = graph[u][j].time;
			if(d2[u] + time < d2[v]) {
				return false;
			}
		}
	}
	return true;
}

void dfs2(int nowVisit) {
	tempPath2.push_back(nowVisit);
	if(nowVisit == source) {
		int value2 = tempPath2.size();
		if(value2 < optValue2) {
			optValue2 = value2;
			path2 = tempPath2;
		}
		tempPath2.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre2[nowVisit].begin(); it != pre2[nowVisit].end(); it++) {
		dfs2(*it);
	}
	tempPath2.pop_back();
}

C++解题报告:

PAT-ADVANCED1111——Online Map

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

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

C++代码:

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

using namespace std;

struct node {
	int v;	//端点编号
	int length;	//道路长度
	int time;	//道路时间
	node(int _v, int _length, int _time) : v(_v), length(_length), time(_time) {};	//构造函数
};

int N;	//十字路口数量
int M;	//道路数量
int source;
int destination;
int INF = 1000000000;
vector<node> graph[501];	//有向图
int d1[501];
int d2[501];
set<int> pre1[501];
set<int> pre2[501];
vector<int> tempPath1;
vector<int> tempPath2;
vector<int> path1;
vector<int> path2;
int optValue1 = INF;
int optValue2 = INF;
bool inq1[501] = {false};
bool inq2[501] = {false};
int countInq1[501] = {0};
int countInq2[501] = {0};

bool spfa1(int s);
void dfs1(int nowVisit);
bool spfa2(int s);
void dfs2(int nowVisit);

int main() {
	cin >> N >> M;
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		graph[V1].push_back(node(V2, length, time));
		if(one_way == 0) {
			graph[V2].push_back(node(V1, length, time));
		}
	}
	cin >> source >> destination;
	spfa1(source);
	dfs1(destination);
	spfa2(source);
	dfs2(destination);
	bool flag = true;
	if(path1.size() != path2.size()) {
		flag = false;
	} else {
		for(int i = 0; i < path1.size(); i++) {
			if(path1[i] != path2[i]) {
				flag = false;
				break;
			}
		}
	}
	if(flag) {
		cout << "Distance = " << d1[destination] << "; Time = " << d2[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	} else {
		cout <<"Distance = " << d1[destination] << ": ";
		for(int i = path1.size() - 1; i >= 0; i--) {
			cout << path1[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
		cout <<"Time = " << d2[destination] << ": ";
		for(int i = path2.size() - 1; i >= 0; i--) {
			cout << path2[i];
			if(i != 0) {
				cout << " -> ";
			}
		}
		cout << endl;
	}
	return 0;
}

bool spfa1(int s) {
	for(int i = 0; i < N; i++) {
		d1[i] = INF;
	}
	d1[s] = 0;
	queue<int> q;
	q.push(s);
	inq1[s] = true;
	countInq1[s]++;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inq1[u] = false;
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int length = graph[u][j].length;
			if(d1[u] + length < d1[v]) {
				d1[v] = d1[u] + length;
				pre1[v].clear();
				pre1[v].insert(u);
				if(!inq1[v]) {
					q.push(v);
					inq1[v] = true;
					countInq1[v]++;
					if(countInq1[v] > N) {
						return false;
					}
				}
			} else if(d1[u] + length == d1[v]) {
				pre1[v].insert(u);
				if(!inq1[v]) {
					q.push(v);
					inq1[v] = true;
					countInq1[v]++;
					if(countInq1[v] > N) {
						return false;
					}
				}
			}
		}
	}
	return true;
}

void dfs1(int nowVisit) {
	tempPath1.push_back(nowVisit);
	if(nowVisit == source) {
		int value1 = 0;
		for(int i = tempPath1.size() - 1; i > 0; i--) {
			//对于邻接表求边权,需要遍历该点连接的所有点才能寻找到所要求的那条边的边权
			for(int j = 0; j < graph[tempPath1[i]].size(); j++) {
				if(graph[tempPath1[i]][j].v == tempPath1[i - 1]) {
					value1 += graph[tempPath1[i]][j].time;
					break;
				}
			}
		}
		if(value1 < optValue1) {
			optValue1 = value1;
			path1 = tempPath1;
		}
		tempPath1.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre1[nowVisit].begin(); it != pre1[nowVisit].end(); it++) {
		dfs1(*it);
	}
	tempPath1.pop_back();
}

bool spfa2(int s) {
	for(int i = 0; i < N; i++) {
		d2[i] = INF;
	}
	d2[s] = 0;
	queue<int> q;
	q.push(s);
	inq2[s] = true;
	countInq2[s]++;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inq2[u] = false;
		for(int j = 0; j < graph[u].size(); j++) {
			int v = graph[u][j].v;
			int time = graph[u][j].time;
			if(d2[u] + time < d2[v]) {
				d2[v] = d2[u] + time;
				pre2[v].clear();
				pre2[v].insert(u);
				if(!inq2[v]){
					q.push(v);
					inq2[v] = true;
					countInq2[v]++;
					if(countInq2[v] > N){
						return false;
					}
				}
			} else if(d2[u] + time == d2[v]) {
				pre2[v].insert(u);
				if(!inq2[v]){
					q.push(v);
					inq2[v] = true;
					countInq2[v]++;
					if(countInq2[v] > N){
						return false;
					}
				}
			}
		}
	}
	return true;
}

void dfs2(int nowVisit) {
	tempPath2.push_back(nowVisit);
	if(nowVisit == source) {
		int value2 = tempPath2.size();
		if(value2 < optValue2) {
			optValue2 = value2;
			path2 = tempPath2;
		}
		tempPath2.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = pre2[nowVisit].begin(); it != pre2[nowVisit].end(); it++) {
		dfs2(*it);
	}
	tempPath2.pop_back();
}

C++解题报告:

PAT-ADVANCED1111——Online Map