地铁
时限:最多 40 个用例,2.0 秒 (C/C++),3.0 秒 (Java)
Sherlock 居住的城市有 N 个地铁站。地铁站从 1 到 N 进行编号,地铁线路从 1 到 M 进行编号。给定地铁站数量 N、地铁线路数量 M、出发站和目的地站的组合数量 Q,求从出发站上车到目的地站下车所需的最少换乘次数总和。
[图]
假设地铁线路图如 [图] 所示,出发站 S 为 2,目的地站 E 为 17。从 S 到 E 的最少换乘次数为 3(2 → 7(换乘) → 13(换乘) → 16(换乘) → 17)
[限制条件]
1.地铁站数量 N 为介于 3 到 100,000 之间的整数。
2.地铁线路数量 M 为介于 2 到 300 之间的整数。
3.出发站和目的地站的组合数量 Q 为介于 1 到 10,000 之间的整数。
4.出发站 S 和目的地站 E 的编号为介于 1 到 N 的整数。
5.每条地铁线路上的地铁站点数量 C 为介于 2 到 N 之间的整数。
6.每条地铁线路都是一条连续的线路,正反两个方向皆可行驶。
7.经过同一个换乘站的地铁线路最大数量为 10 。
8.从任意地铁站点总是可以前往其他所有站点。
[输入]
首先,给定测试用例数量 T,后面接着输入 T 种测试用例。
每个测试用例的第一行,给定地铁站数量 N、地铁线路数量 M、出发站和目的站组合数量 Q。
接下来的 M 行,每行给定一条地铁线路上地铁站数量 C,以及 C 个地铁站的编号,以空格分隔。
接下来的 Q 行,每行给定一对想要求得其最小换乘次数的出发站 S 和目的站 E ,以空格分隔。
[输出]
每行输出一个测试用例。每个测试用例,都输出“#x”(x 为测试用例的编号,从 1 开始),以及从出发站到目的站所需的最少换乘次数总和。
[输入和输出示例]
(输入)
3
18 5 3
7 2 3 4 7 10 9 8
4 1 3 6 9
5 5 6 7 13 14
4 12 13 15 16
4 11 16 17 18
1 8
4 14
2 17
32 5 8
4 19 7 4 1
10 28 5 24 18 10 16 31 2 3 19
8 27 6 21 23 17 20 29 19
9 9 12 8 25 11 14 26 22 19
5 13 30 15 32 19
17 11
25 23
20 8
3 8
18 32
28 16
14 12
20 21
36 5 7
8 4 7 33 34 17 21 9 3
7 8 16 32 2 22 19 3
10 30 14 5 26 24 27 20 36 12 4
8 10 31 23 29 11 18 35 16
7 1 25 28 6 15 13 9
30 1
9 7
6 14
2 31
28 4
5 26
23 36
(输出)
#1 5
#2 5
#3 9
解决思路:将线路抽象成点,将换乘站点抽象成边,问题变为求多点到多点的最短路径,且数据量为300,可使用Floyd算法。
package graph; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.StringTokenizer; /** * * 解决思路:将线路抽象成点,将换乘站点抽象成边,问题变为求多点到多点的最短路径,且数据量为300,可使用Floyd算法。 * @author XA-GDD * */ public class Metro_0601 { static int INF = Integer.MAX_VALUE>>1; static int T,N,M,Q,C; static int _Max_N = 100004; static int _Max_M = 304; static int [][] minDist = new int[_Max_M][_Max_M]; static ArrayList stationList = new ArrayList(); static int ANS; public static void main(String[] args) throws IOException { init(); System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\simple_input_0601.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = Integer.parseInt(st.nextToken()); for(int testCase = 1; testCase<=T;testCase++) { st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); Q = Integer.parseInt(st.nextToken()); for(int m=1;m<=M;m++) { //地铁线路 st = new StringTokenizer(br.readLine()); C = Integer.parseInt(st.nextToken()); for(int c=1;c<=C;c++) { //地铁站点 int stationNo = Integer.parseInt(st.nextToken()); stationList.get(stationNo).add(m); //每个站点保存对应的线路 for(int i=0;i<stationList.get(stationNo).size();i++) { //stationList.get(stationNo).size()>0表示当前站点,有多个线路经过,即当前站点为换乘站点 int subwayLine = stationList.get(stationNo).get(i); //站点对应的每个线路 if(subwayLine==m) { //当前线路到当前线路距离为0 minDist[subwayLine][m] = 0; }else {//当前线路到其他线路,距离都设为1 minDist[subwayLine][m] = 1; minDist[m][subwayLine] = 1; } } } } floyd(); //求出任意线路到任意线路的最短路径 for(int q=0;q<Q;q++) { st = new StringTokenizer(br.readLine()); int start = Integer.parseInt(st.nextToken()); int end = Integer.parseInt(st.nextToken()); int seMinDist = INF; //因为给出的数据是站点,而我们求出的是线路到线路的最短路径 //所以要根据站点找到线路,在包含开始站点和结束站点线路的路径中,选择最短的那条 for(int i=0;i<stationList.get(start).size();i++){ int startLine = stationList.get(start).get(i); for(int j=0;j<stationList.get(end).size();j++){ int endLine = stationList.get(end).get(j); seMinDist = Math.min(seMinDist, minDist[startLine][endLine]); } } ANS += seMinDist; } System.out.println("#"+testCase+" "+ANS); clear(); } } static void floyd() { for(int v=1;v<=M;v++) { for(int i=1;i<=M;i++) { for(int j=1;j<=M;j++) { minDist[i][j] = Math.min(minDist[i][j], minDist[i][v]+minDist[v][j]); } } } } static void init() { for(int i=0;i<_Max_N;i++) { stationList.add(new ArrayList()); } for(int i=0;i<_Max_M;i++) { Arrays.fill(minDist[i], INF); } } static void clear() { ANS = 0; for(int i=0;i<=M+1;i++) { Arrays.fill(minDist[i], INF); } for(int i=0;i<=N+1;i++) { stationList.get(i).clear(); } } }