題目大意
給出各地鐵線所經過的站點,構成一張地鐵交通圖。再給出起點和終點,讓你找出最快的一條路徑,如果路徑不唯一就選擇中轉次數最少的那一條。思路解析
本題是鐵了心的要考鄰接表。傳統的Dijkstra + DFS最後一個測試點會超時(末尾也會附上該解法)。本題的難點是判斷中轉站和DFS以及地鐵車次的記錄。下面分三點講解:1. 鄰接表所採用的最短路徑法就是暴力遍歷,用深搜從頭走到尾,也就是說在DFS過程中得出最短路徑。不同於鄰接矩陣,鄰接表沿着當前節點的出度逐項順移,所以不同路徑的相同節點必然會被遍歷多次,但是要保證本次遍歷不能兜圈子,所以visited具有本次有效性。
2. 由於需要統計中轉站,所以每個節點必須要記錄所通車次,所以需要用map記錄,考慮到中轉站所以要結合前一個節點綜合得出key,value對應車次。
3. 中轉站的判斷要通過最終路徑res得出,用O(n)的循環判斷,i從1開始,判斷res[i-1]→res[i]的車次是否和res[i-2]到res[i-1]的車次一致,可以把後者的車次計做preLine.每一次用res[i-1]→res[i]和preLine比較,不同則說明換站。
示例代碼
#include<iostream>
#include<vector>
#include<map>
#define INF (~(0x1<<31))
using namespace std;
vector<vector<int>> gra(10000, vector<int>());//鄰接矩陣存儲
map<int, int> line;
int visited[10000];
int start, end1, minsize = INF,mincnt = INF;
vector<int> temp, res;
void dfs(int v) {//根據圖順移,每個點必然遍歷多次,但要保證本次遍歷不能出現環路,所以visited具有本次有效性
if (v == end1 && temp.size() <= minsize ) {
int preLine = -1, cnt = 0;
for (int i = 1; i < temp.size(); i++) {//統計中轉站
if (line[temp[i - 1] * 10000 + temp[i]] != preLine) cnt++;
preLine = line[temp[i - 1] * 10000 + temp[i]];
}
if (temp.size() < minsize) {
minsize = temp.size();
mincnt = cnt;
res = temp;
}
else if (temp.size() == minsize && cnt < mincnt) {
mincnt = cnt;
res = temp;
}
return;
}
if (v == end1) return;
for (int i = 0; i < gra[v].size(); i++) {
if (visited[gra[v][i]] == 0) {
visited[gra[v][i]] = 1;
temp.push_back(gra[v][i]);
dfs(gra[v][i]);
temp.pop_back();
visited[gra[v][i]] = 0;//本次遍歷結束,下一條路徑有可能還會用到
}
}
}
int main() {
int n, k;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int m, pre, temp;
scanf("%d %d", &m, &pre);
for (int j = 1; j < m; j++) {
scanf("%d", &temp);
gra[pre].push_back(temp);
gra[temp].push_back(pre);
line[pre * 10000 + temp] = line[temp * 10000 + pre] = i;
pre = temp;
}
}
scanf("%d", &k);
for (int i = 0; i < k; i++) {
scanf("%d %d", &start, &end1);
mincnt = minsize = INF;
visited[start] = 1;
temp.clear();
temp.push_back(start);
dfs(start);
visited[start] = 0;//不要忘記擦屁股
printf("%d\n", res.size() - 1);
int preLine = -1, start = res[0];
for (int j = 1; j < res.size(); j++) {
if (line[res[j - 1] * 10000 + res[j]] != preLine) {
if (preLine != -1)
printf("Take Line#%d from %04d to %04d.\n", preLine, start, res[j-1]);
preLine = line[res[j - 1] * 10000 + res[j]];
start = res[j - 1];
}
}
printf("Take Line#%d from %04d to %04d.\n", preLine, start, end1);
}
return 0;
}
Dijkstra解法(最後一個測試點超時,但最好還是掌握該法)
爲了避免內存超限所以使用了unordered_map,然並卵,該超時的還是超時,unordered_map相比map僅僅是空間上節省了,但由於缺少了紅黑樹的支持,在查找上會不足。#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>
#define INF (~(0x1<<31))
using namespace std;
int n, m, num = 0;
int gra[10000][10000];
unordered_map<int, int> line;//記錄車站通行的車次
vector<vector<int>> dijkstra(int s) {
vector<bool> visited(num,false);
vector<int> dist(num, INF);
vector<vector<int>> path(num);
dist[s] = 0;
for (int i = 0; i < num; i++) {
int min = INF, u = -1;
for (int j = 0; j < num; j++) {
if (!visited[j] && dist[j] < min) {
min = dist[j];
u = j;
}
}
if (u != -1) {
visited[u] = true;
}
for (int j = 0; j < num; j++) {
if (!visited[j] && gra[u][j] != INF) {
if (dist[u] + gra[u][j] < dist[j]) {
path[j].clear();
path[j].push_back(u);
dist[j] = dist[u] + gra[u][j];
}
else if (dist[u] + gra[u][j] == dist[j]) {
path[j].push_back(u);
}
}
}
}
return path;
}
int s, mincnt = INF;//s作爲起始點
vector<int> temp,res;
void dfs(int v, vector<vector<int>> path) {
temp.push_back(v);
if (v == s) {
int cnt = -1, preLine = 0;
for (int i = 1; i < temp.size(); i++) {
if (line[temp[i - 1] * 10000 + temp[i]] != preLine) cnt++;
preLine = line[temp[i - 1] * 10000 + temp[i]];
}
if (cnt < mincnt) {
mincnt = cnt;
res = temp;
}
temp.pop_back();
return;
}
for (int i = 0; i < path[v].size(); i++) {
dfs(path[v][i], path);
}
temp.pop_back();
}
unordered_map<int, int> mapp;//通過四位索引換算序號
unordered_map<int, int> rmapp;//供輸出換算
int main() {
scanf("%d", &n);
fill(gra[0], gra[0] + 1010 * 1010, INF);
for (int i = 1; i <= n; i++) {
scanf("%d", &m);
int a;
scanf("%d", &a);
if (mapp.find(a) == mapp.end()) {
rmapp[num] = a;
mapp[a] = num++;
}
a = mapp[a];
for (int j = 1; j < m; j++) {
int b;
scanf("%d", &b);
if (mapp.find(b) == mapp.end()) {
rmapp[num] = b;
mapp[b] = num++;
}
b = mapp[b];
line[a * 10000 + b] = line[b * 10000 + a] = i;
gra[a][b] = gra[b][a] = 1;
a = b;
}
}
int k;
scanf("%d", &k);
for (int i = 0; i < k; i++) {
int a, b;
scanf("%d %d", &a, &b);
a = mapp[a];
b = mapp[b];
vector<vector<int>> path = dijkstra(a);
s = a;
res.clear();
mincnt = INF;
dfs(b, path);//從終點倒着回溯
printf("%d\n", res.size() - 1);
int preLine = -1, start = res[res.size() - 1];
for (int j = res.size() - 2; j >= 0; j--) {
if (line[res[j + 1] * 10000 + res[j]] != preLine) {
if (preLine != -1) {
printf("Take Line#%d from %04d to %04d.\n", preLine, rmapp[start], rmapp[res[j + 1]]);
}
preLine = line[res[j + 1] * 10000 + res[j]];
start = res[j + 1];
}
}
printf("Take Line#%d from %04d to %04d.\n", preLine, rmapp[start], rmapp[b]);
}
return 0;
}