题目:https://pintia.cn/problem-sets/994805342720868352/problems/994805347523346432
这个题思路说不上很难,但是在考试时间的限制下真的是压力山大,十万条命接不起。本人用的两次DFS,一次找最短路径。第二次通过最少的换乘数找最佳路径。感觉比较难的点,是保存几号线以及中转站。别忽略地铁是双向的。该算法要剪枝,不然要超时。因为地铁是双向的,所以第二个DFS找最佳路径时,可以把终点想成起点,然后乘坐几号线去某个中转站(或终点)。所以用一个结构体,记录这两个数据。
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 10010;
const int INF = 1e9;
int n, m, k, st, ed;
bool vis[maxn] = {false};
int d[maxn], line[maxn][maxn]; //d记录最短路长,line记录a到b是几号线(其实a,b是临近的两站)。
vector<int> sta[maxn]; //记录该站可以通往哪些站
vector<int> pre[maxn]; //记录前驱路径
void DFS(int u){
if(vis[u]){
return;
}
vis[u] = true;
for(int i = 0; i < sta[u].size(); i++){
int v = sta[u][i];
if(!vis[v]){
d[v] = d[u] + 1;
pre[v].push_back(u);
}
else{
if(d[v] > d[u] + 1){
d[v] = d[u] + 1;
vis[v] = false;//为了更新之后的所有最短路长
pre[v].clear();
pre[v].push_back(u);
}
else if(d[v] == d[u] + 1)
pre[v].push_back(u);
}
DFS(v);
}
}
struct Node{ //用来记录中转站和几号线
int trans;
int take;
Node (int a, int b) : trans(a), take(b){};
};
vector<Node> path, temp;
int now, minnum; //now是当前是几号线,minnum是最小换乘次数
void DFS2(int u, int num){
if(u == st){
if(num < minnum){
minnum = num;
path = temp;
}
return;
}
for(int i = 0; i < pre[u].size(); i++){
int v = pre[u][i];
if(u == ed){ //第一个点进行初始化
temp.clear();
now = line[u][v];
temp.push_back(Node(u, now));
}
if(line[u][v] != now){
if(num + 1 > minnum) //剪枝
continue;
int oldnow = now; //oldnow和num一定要还原!!!
now = line[u][v];
temp.push_back(Node(u, now));
DFS2(v, ++num);
temp.pop_back();
num--;
now = oldnow;
}
else if(num < minnum){ //剪枝
DFS2(v, num);
}
}
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &m);
int pre;
for(int j = 0; j < m; j++){
int stop;
scanf("%d", &stop);
if(j){
sta[pre].push_back(stop);
sta[stop].push_back(pre);
line[pre][stop] = i;
line[stop][pre] = i;
}
pre = stop;
}
}
scanf("%d", &k);
for(int i = 0; i < k; i++){
scanf("%d%d", &st, &ed);
fill(vis, vis+maxn, 0);
fill(d, d+maxn, INF);
d[st] = 0;
for(int j = 0; j < maxn; j++){//初始化上一次的前驱节点
pre[j].clear();
}
DFS(st);
printf("%d\n", d[ed]);
minnum = INF;
DFS2(ed, 0);
int s1, s2;
for(int i = path.size() - 1; i >= 0; i--){
if(i == path.size() - 1){
s1 = st;
s2 = path[i].trans;
}else{
s1 = path[i+1].trans;
s2 = path[i].trans;
}
printf("Take Line#%d from %04d to %04d.\n", path[i].take, s1, s2);
}
}
return 0;
}