一、题目概述
依据给定地铁路线图,找到源点、汇点间经过站数最少的路线,若两条路线经过站数相同,选择需换乘数少者。
二、思路
1、关于如何判断a-b两站间的路线是几号地铁线路:
开辟一个10000 * 10000单元的数组是不现实的,空间效率很低。因此采用边界散列的思维,站点最大10000,则a * 10000 + b和b * 10000 + a可以唯一标识a-b间的路径。因此a-b所在的地铁线路号存储在line[ a * 10000 + b]和line[b * 10000 + a]中。
2、求解最短路径
方案是多样的:
(1)利用BFS按层访问的性质,找到所有经过结点数相同的源点、汇点间的路径,再对路径dfs选择出换乘最少的路径。
(2)dijkstra算法求最短路径,在对路径dfs选择换乘最少的路径。
(3)dfs遍历所有到达汇点的路径,比较路径上的结点数、换乘数,选择路径。
出于代码量的考虑,选择方案3。
三、代码
#include <cstdio>
#include <vector>
#include <unordered_map>
using namespace std;
vector<int> G[10000], path, a, visited(1e4, 0);
unordered_map<int, int> line;
int N, K, Min;
void dfs(int v, int d)
{
visited[v] = 1;
a.push_back(v);
if(v == d)
{
int cnt = 0;
for(int i = 1; i < a.size() - 1; ++i)
if(line[a[i - 1] * 1e4 + a[i]] != line[a[i] * 1e4 + a[i + 1]])
++cnt;
if(!path.size() || a.size() < path.size() || (a.size() == path.size() && cnt < Min))
{
path = a;
Min = cnt;
}
}
else for(int i = 0; i < G[v].size(); ++i)
if(!visited[G[v][i]] && (!path.size() || (path.size() && a.size() < path.size())))
dfs(G[v][i], d);
visited[v] = 0;
a.pop_back();
}
int main()
{
scanf("%d", &N);
for(int i = 1, M, pre, v; i <= N; ++i)
{
scanf("%d %d", &M, &pre);
for(int j = 1; j < M; ++j)
{
scanf("%d", &v);
G[pre].push_back(v);
G[v].push_back(pre);
line[pre * 1e4 + v] = line[v * 1e4 + pre] = i;
pre = v;
}
}
scanf("%d", &K);
for(int i = 0, s, d; i < K; ++i)
{
scanf("%d %d", &s, &d);
path.clear();
dfs(s, d);
printf("%d\n", path.size() - 1);
for(int j = 1, pre = path[0]; j < path.size(); ++j)
if(j + 1 == path.size() || line[path[j - 1] * 1e4 + path[j]] != line[path[j] * 1e4 + path[j + 1]])
{
printf("Take Line#%d from %04d to %04d.\n", line[path[j - 1] * 1e4 + path[j]], pre, path[j]);
pre = path[j];
}
}
}