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