1. 程式人生 > >PAT-ADVANCED1111——Online Map

PAT-ADVANCED1111——Online Map

我的PAT-ADVANCED程式碼倉:https://github.com/617076674/PAT-ADVANCED

原題連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805358663417856

題目描述:

題目翻譯:

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++解題報告:

思路二: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++解題報告:

思路三:SPFA演算法+深度優先遍歷(鄰接表實現)

期望時間複雜度是O(kM),其中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++解題報告:

思路四:Dijkstra演算法+深度優先遍歷(鄰接矩陣實現)

分別以長度和時間為邊權建立兩張圖,求兩次Dijkstra演算法+深度優先遍歷即可。

時間複雜度和空間複雜度均是O(N ^ 2)。

C++程式碼:

#include<iostream>
#include<vector>

using namespace std;

int N;
int M;
int INF = 1000000000;
int lenGraph[510][510];
int timeGraph[510][510];
int source;
int destination;
int dLen[510];
bool visitedLen[510] = {false};
vector<int> preLen[510];
vector<int> tempPathLen;
vector<int> pathLen;
int minTime = INF;
int dTime[510];
bool visitedTime[510] = {false};
vector<int> preTime[510];
vector<int> tempPathTime;
vector<int> pathTime;
int minCross = INF;

void dijkstraLen(int s);
void dfsLen(int nowVisit);

void dijkstraTime(int s);
void dfsTime(int nowVisit);

int main() {
	cin >> N >> M;
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			lenGraph[i][j] = timeGraph[i][j] = INF;
		}
	}
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		lenGraph[V1][V2] = length;
		timeGraph[V1][V2] = time;
		if(one_way != 1) {
			lenGraph[V2][V1] = length;
			timeGraph[V2][V1] = time;
		}
	}
	cin >> source >> destination;
	dijkstraLen(source);
	dfsLen(destination);
	dijkstraTime(source);
	dfsTime(destination);
	if(pathLen == pathTime) {
		printf("Distance = %d; Time = %d:", dLen[destination], dTime[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	} else {
		printf("Distance = %d:", dLen[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
		printf("Time = %d:", dTime[destination]);
		for(int i = pathTime.size() - 1; i >= 0; i--) {
			printf(" %d", pathTime[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	}

}

void dijkstraLen(int s) {
	for(int i = 0; i < N; i++) {
		dLen[i] = INF;
	}
	dLen[s] = 0;
	for(int i = 0; i < N; i++) {
		int u = -1, min = INF;
		for(int j = 0; j < N; j++) {
			if(!visitedLen[j] && dLen[j] < min) {
				min = dLen[j];
				u = j;
			}
		}
		if(u == -1) {
			return;
		}
		visitedLen[u] = true;
		for(int v = 0; v < N; v++) {
			if(!visitedLen[v] && lenGraph[u][v] != INF) {
				if(dLen[u] + lenGraph[u][v] < dLen[v]) {
					dLen[v] = dLen[u] + lenGraph[u][v];
					preLen[v].clear();
					preLen[v].push_back(u);
				} else if(dLen[u] + lenGraph[u][v] == dLen[v]) {
					preLen[v].push_back(u);
				}
			}
		}
	}
}

void dfsLen(int nowVisit) {
	tempPathLen.push_back(nowVisit);
	if(nowVisit == source) {
		int time = 0;
		for(int i = tempPathLen.size() - 1; i > 0; i--) {
			time += timeGraph[tempPathLen[i]][tempPathLen[i - 1]];
		}
		if(time < minTime) {
			pathLen = tempPathLen;
			minTime = time;
		}
		tempPathLen.pop_back();
		return;
	}
	for(int i = 0; i < preLen[nowVisit].size(); i++) {
		dfsLen(preLen[nowVisit][i]);
	}
	tempPathLen.pop_back();
}

void dijkstraTime(int s) {
	for(int i = 0; i < N; i++) {
		dTime[i] = INF;
	}
	dTime[s] = 0;
	for(int i = 0; i < N; i++) {
		int u = -1, min = INF;
		for(int j = 0; j < N; j++) {
			if(!visitedTime[j] && dTime[j] < min) {
				min = dTime[j];
				u = j;
			}
		}
		if(u == -1) {
			return;
		}
		visitedTime[u] = true;
		for(int v = 0; v < N; v++) {
			if(!visitedTime[v] && timeGraph[u][v] != INF) {
				if(dTime[u] + timeGraph[u][v] < dTime[v]) {
					dTime[v] = dTime[u] + timeGraph[u][v];
					preTime[v].clear();
					preTime[v].push_back(u);
				} else if(dTime[u] + timeGraph[u][v] == dTime[v]) {
					preTime[v].push_back(u);
				}
			}
		}
	}
}

void dfsTime(int nowVisit) {
	tempPathTime.push_back(nowVisit);
	if(nowVisit == source) {
		if(tempPathTime.size() < minCross) {
			pathTime = tempPathTime;
			minCross = tempPathTime.size();
		}
		tempPathTime.pop_back();
		return;
	}
	for(int i = 0; i < preTime[nowVisit].size(); i++) {
		dfsTime(preTime[nowVisit][i]);
	}
	tempPathTime.pop_back();
}

C++解題報告:

思路五:Bellman-Ford演算法+深度優先遍歷(鄰接矩陣實現)(測試點4會超時)

分別以長度和時間為邊權建立兩張圖,求兩次Bellman-Ford演算法+深度優先遍歷即可。

時間複雜度是O(N ^ 3)。空間複雜度是O(N ^ 2)。

C++程式碼:

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

using namespace std;

int N;
int M;
int INF = 1000000000;
int lenGraph[510][510];
int timeGraph[510][510];
int source;
int destination;
int dLen[510];
set<int> preLen[510];
vector<int> tempPathLen;
vector<int> pathLen;
int minTime = INF;
int dTime[510];
set<int> preTime[510];
vector<int> tempPathTime;
vector<int> pathTime;
int minCross = INF;

bool bellmanFordLen(int s);
void dfsLen(int nowVisit);

bool bellmanFordTime(int s);
void dfsTime(int nowVisit);

int main() {
	cin >> N >> M;
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			lenGraph[i][j] = timeGraph[i][j] = INF;
		}
	}
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		lenGraph[V1][V2] = length;
		timeGraph[V1][V2] = time;
		if(one_way != 1) {
			lenGraph[V2][V1] = length;
			timeGraph[V2][V1] = time;
		}
	}
	cin >> source >> destination;
	bellmanFordLen(source);
	dfsLen(destination);
	bellmanFordTime(source);
	dfsTime(destination);
	if(pathLen == pathTime) {
		printf("Distance = %d; Time = %d:", dLen[destination], dTime[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	} else {
		printf("Distance = %d:", dLen[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
		printf("Time = %d:", dTime[destination]);
		for(int i = pathTime.size() - 1; i >= 0; i--) {
			printf(" %d", pathTime[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	}

}

bool bellmanFordLen(int s) {
	for(int i = 0; i < N; i++) {
		dLen[i] = INF;
	}
	dLen[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(lenGraph[u][v] != INF) {
					if(dLen[u] + lenGraph[u][v] < dLen[v]) {
						dLen[v] = dLen[u] + lenGraph[u][v];
						preLen[v].clear();
						preLen[v].insert(u);
					} else if(dLen[u] + lenGraph[u][v] == dLen[v]) {
						preLen[v].insert(u);
					}
				}
			}
		}
	}
	for(int u = 0; u < N; u++) {
		for(int v = 0; v < N; v++) {
			if(lenGraph[u][v] != INF) {
				if(dLen[u] + lenGraph[u][v] < dLen[v]) {
					return false;
				}
			}
		}
	}
	return true;
}

void dfsLen(int nowVisit) {
	tempPathLen.push_back(nowVisit);
	if(nowVisit == source) {
		int time = 0;
		for(int i = tempPathLen.size() - 1; i > 0; i--) {
			time += timeGraph[tempPathLen[i]][tempPathLen[i - 1]];
		}
		if(time < minTime) {
			pathLen = tempPathLen;
			minTime = time;
		}
		tempPathLen.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = preLen[nowVisit].begin(); it != preLen[nowVisit].end(); it++){
		dfsLen(*it);
	}
	tempPathLen.pop_back();
}

bool bellmanFordTime(int s) {
	for(int i = 0; i < N; i++) {
		dTime[i] = INF;
	}
	dTime[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(timeGraph[u][v] != INF) {
					if(dTime[u] + timeGraph[u][v] < dTime[v]) {
						dTime[v] = dTime[u] + timeGraph[u][v];
						preTime[v].clear();
						preTime[v].insert(u);
					} else if(dTime[u] + timeGraph[u][v] == dTime[v]) {
						preTime[v].insert(u);
					}
				}
			}
		}
	}
	for(int u = 0; u < N; u++) {
		for(int v = 0; v < N; v++) {
			if(lenGraph[u][v] != INF) {
				if(dLen[u] + lenGraph[u][v] < dLen[v]) {
					return false;
				}
			}
		}
	}
	return true;
}

void dfsTime(int nowVisit) {
	tempPathTime.push_back(nowVisit);
	if(nowVisit == source) {
		if(tempPathTime.size() < minCross) {
			pathTime = tempPathTime;
			minCross = tempPathTime.size();
		}
		tempPathTime.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = preTime[nowVisit].begin(); it != preTime[nowVisit].end(); it++){
		dfsTime(*it);
	}
	tempPathTime.pop_back();
}

C++解題報告:

思路六:SPFA演算法+深度優先遍歷(鄰接矩陣實現)

期望時間複雜度是O(kN),其中k是一個常數,在很多情況下k不超過2,可見這個演算法異常高效,並且經常性地優於堆優化的Dijkstra演算法。空間複雜度是O(N ^ 2)。

C++程式碼:

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

using namespace std;

int N;
int M;
int INF = 1000000000;
int lenGraph[510][510];
int timeGraph[510][510];
int source;
int destination;
int dLen[510];
set<int> preLen[510];
vector<int> tempPathLen;
vector<int> pathLen;
int minTime = INF;
bool inqLen[510] = {false};
int countInqLen[510] = {0};
int dTime[510];
set<int> preTime[510];
vector<int> tempPathTime;
vector<int> pathTime;
int minCross = INF;
bool inqTime[510] = {false};
int countInqTime[510] = {0};

bool spfaLen(int s);
void dfsLen(int nowVisit);

bool spfaTime(int s);
void dfsTime(int nowVisit);

int main() {
	cin >> N >> M;
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			lenGraph[i][j] = timeGraph[i][j] = INF;
		}
	}
	int V1, V2, one_way, length, time;
	for(int i = 0; i < M; i++) {
		cin >> V1 >> V2 >> one_way >> length >> time;
		lenGraph[V1][V2] = length;
		timeGraph[V1][V2] = time;
		if(one_way != 1) {
			lenGraph[V2][V1] = length;
			timeGraph[V2][V1] = time;
		}
	}
	cin >> source >> destination;
	spfaLen(source);
	dfsLen(destination);
	spfaTime(source);
	dfsTime(destination);
	if(pathLen == pathTime) {
		printf("Distance = %d; Time = %d:", dLen[destination], dTime[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	} else {
		printf("Distance = %d:", dLen[destination]);
		for(int i = pathLen.size() - 1; i >= 0; i--) {
			printf(" %d", pathLen[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
		printf("Time = %d:", dTime[destination]);
		for(int i = pathTime.size() - 1; i >= 0; i--) {
			printf(" %d", pathTime[i]);
			if(i != 0) {
				printf(" ->");
			}
		}
		printf("\n");
	}

}

bool spfaLen(int s) {
	for(int i = 0; i < N; i++) {
		dLen[i] = INF;
	}
	dLen[s] = 0;
	queue<int> q;
	q.push(s);
	inqLen[s] = true;
	countInqLen[s]++;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inqLen[u] = false;
		for(int v = 0; v < N; v++) {
			if(lenGraph[u][v] != INF) {
				if(dLen[u] + lenGraph[u][v] < dLen[v]) {
					dLen[v] = dLen[u] + lenGraph[u][v];
					preLen[v].clear();
					preLen[v].insert(u);
					if(!inqLen[v]){
						q.push(v);
						inqLen[v] = true;
						countInqLen[v]++;
						if(countInqLen[v] >= N){
							return false;
						}
					}
				}else if(dLen[u] + lenGraph[u][v] == dLen[v]) {
					preLen[v].insert(u);
					if(!inqLen[v]){
						q.push(v);
						inqLen[v] = true;
						countInqLen[v]++;
						if(countInqLen[v] >= N){
							return false;
						}
					}
				}
			}
		}
	}
	return true;
}

void dfsLen(int nowVisit) {
	tempPathLen.push_back(nowVisit);
	if(nowVisit == source) {
		int time = 0;
		for(int i = tempPathLen.size() - 1; i > 0; i--) {
			time += timeGraph[tempPathLen[i]][tempPathLen[i - 1]];
		}
		if(time < minTime) {
			pathLen = tempPathLen;
			minTime = time;
		}
		tempPathLen.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = preLen[nowVisit].begin(); it != preLen[nowVisit].end(); it++) {
		dfsLen(*it);
	}
	tempPathLen.pop_back();
}

bool spfaTime(int s) {
	for(int i = 0; i < N; i++) {
		dTime[i] = INF;
	}
	dTime[s] = 0;
	queue<int> q;
	q.push(s);
	inqTime[s] = true;
	countInqTime[s]++;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		inqTime[u] = false;
		for(int v = 0; v < N; v++) {
			if(timeGraph[u][v] != INF) {
				if(dTime[u] + timeGraph[u][v] < dTime[v]) {
					dTime[v] = dTime[u] + timeGraph[u][v];
					preTime[v].clear();
					preTime[v].insert(u);
					if(!inqTime[v]){
						q.push(v);
						inqTime[v] = true;
						countInqTime[v]++;
						if(countInqTime[v] >= N){
							return false;
						}
					}
				}else if(dTime[u] + timeGraph[u][v] == dTime[v]) {
					preTime[v].insert(u);
					if(!inqTime[v]){
						q.push(v);
						inqTime[v] = true;
						countInqTime[v]++;
						if(countInqTime[v] >= N){
							return false;
						}
					}
				}
			}
		}
	}
	return true;
}

void dfsTime(int nowVisit) {
	tempPathTime.push_back(nowVisit);
	if(nowVisit == source) {
		if(tempPathTime.size() < minCross) {
			pathTime = tempPathTime;
			minCross = tempPathTime.size();
		}
		tempPathTime.pop_back();
		return;
	}
	set<int>::iterator it;
	for(it = preTime[nowVisit].begin(); it != preTime[nowVisit].end(); it++) {
		dfsTime(*it);
	}
	tempPathTime.pop_back();
}

C++解題報告: