|
题目大意
给出各地铁线所经过的站点,构成一张地铁交通图。再给出起点和终点,让你找出最快的一条路径,如果路径不唯一就选择中转次数最少的那一条。思路解析
本题是铁了心的要考邻接表。传统的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;
}