出行 - Travel - Dijkstra
出行
时间限制:最多X个用例,2秒(C/C++),2.5秒(Java)
当前有一个N行M列网格模型的国家。每个单元格代表着一座城市。每次通过一座城市时,都会产生一笔通行费。
今年夏天将在这个国家的某座城市举办一个盛大的庆典活动。约翰和克洛伊为了参加这个庆典,他们需要各自从自己居住的城市出发,前往到举办庆典活动的城市。前往时,可以移动到上下左右相邻的城市,一旦离开一座城市,就需要立即支付通行费。此外,由于通行费是按团体收费而不是按人数收费,因此在移动的途中,在某一座城市碰面,然后一同出行要比单独出行花费的更少。
<图 1> <图 2>
如上图所示,假设约翰居住在[2,2],克洛伊居住在[2,5],举办庆典的城市位于 [5,4]。(这时,坐标中第一个数字表示行数,第二个数字表示列数,并且每个单元格中的数字表示通行费。)约翰前往举办庆典城市的最低费用路径为<图1>的蓝色路径,该费用为18,克洛伊的情况,最低费用路径为绿色路径,该费用为15。如果按照<图2>一样,约翰和克洛伊在[2,4]上碰面,然后一起移动时,费用会变为10 + 3 + (4 + 9 + 1) = 27,这时的费用为最低。
※ 举办庆典活动的城市不收取通行费;约翰和克洛伊共同经过的城市,可以一起支付通行费。
帮助约翰和克洛伊求出俩人移动到举办庆典的城市时,所需要的最低费用。
[限制条件]
1.网格的行数N和列数M为介于3到500之间的整数。(3 <= N, M <= 500)
2.各城市的通行费C为介于0到10,000之间的整数。(0 <= C <= 10,000)
3.即使经过的是曾经到过的城市,但仍然需要再次支付通行费。
4.如果俩人一起同行,那么仅收取一次的通行费。
5.举办庆典活动的城市不收取通行费。如果他们在出发城市碰面,然后一起同行,那么会收取通行费,但仅需支付一人的费用。
6.约翰居住的城市、克洛伊居住的城市以及举办庆典活动的城市为三座不同的城市。
[输入]
第一行给出测试用例数量T,接着给出T种测试用例。第一行空格区分给出网格的行数N和列数 M、约翰出发城市的行数R1和列数C1、克鲁伊出发城市的行数R2和列数C2以及举办庆典活动的行数R3和列数C3。接下来通过N行,每行空格区分给出M个城市的通行费(C)。
[输出]
一个测试用例输出一行。每个测试用例都输出“#x”(其中x表示测试用例编号,从1开始),加一个空格后,输出约翰和克洛伊前往举办庆典活动的城市时,所需要的最低费用。
[输入输出 示例]
(输入)
3
5 6 2 2 2 5 5 4
1 0 1 5 3 2
2 7 3 4 3 2
3 2 5 9 8 7
1 2 8 1 3 2
6 5 2 3 4 1
6 9 1 9 6 1 4 6
7 7 5 2 7 4 7 6 1
6 4 9 4 10 3 4 7 5
10 9 8 4 10 8 3 10 10
8 7 4 8 2 2 2 2 2
1 3 9 6 9 1 6 1 9
6 4 3 10 5 7 8 7 2
5 5 3 3 3 4 3 5
4 5 9 1 8
1 9 3 1 8
2 1 3 5 10
3 6 6 2 7
2 8 8 0 8
(输出)
#1 27
#2 53
#3 8
思路分析:
问题可概括为:A点,B点在D点汇合,然后到达C点
分别求出A,B,C点到所有点的最短路径,每个点i的最短路径=A到i+B到i+C到i-2*当前点i的权值-C点的权值
然后遍历所有点,求出距离最短的路径
package pro.dijkstra; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; import java.util.StringTokenizer; /** * 思路分析: * 问题可概括为:A点,B点在D点汇合,然后到达C点 * 分别求出A,B,C点到所有点的最短路径,每个点i的最短路径=A到i+B到i+C到i-2*当前点i的权值-C点的权值 * 然后遍历所有点,求出距离最短的路径 * @author XA-GDD * */ public class Travel_0622 { static int T,N,M,R1,C1,R2,C2,R3,C3; static int _max_Nval = 500; static int _max_Mval = 500; static int [][] cost = new int[_max_Nval+1][_max_Mval+1]; static int [][][] minCost = new int [_max_Nval+1][_max_Mval+1][4]; //最小花费 static int [][] visited = new int[_max_Nval+1][_max_Mval+1]; static int [][] dir = {{-1,0},{0,1},{1,0},{0,-1}}; //方向 static long ANS; public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\eclipse-workspace\\sw_pro\\test_case\\sample_input_0622.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++) { init(); st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); M = Integer.parseInt(st.nextToken()); R1 = Integer.parseInt(st.nextToken()); C1 = Integer.parseInt(st.nextToken()); R2 = Integer.parseInt(st.nextToken()); C2 = Integer.parseInt(st.nextToken()); R3 = Integer.parseInt(st.nextToken()); C3 = Integer.parseInt(st.nextToken()); init(); for(int i=1;i<=N;i++) { st = new StringTokenizer(br.readLine()); for(int j=1;j<=M;j++) { cost[i][j] = Integer.parseInt(st.nextToken()); } } dijkstra(new int[] {R1,C1},1); //(R1,C1)点到所有点的最短距离 dijkstra(new int[] {R2,C2},2); //(R2,C2)点到所有点的最短距离 dijkstra(new int[] {R3,C3},3); //(R3,C3)点到所有点的最短距离 for(int i=1;i<=N;i++) { for(int j=1;j<=M;j++) { ANS = Math.min(ANS, minCost[i][j][1]+minCost[i][j][2]+minCost[i][j][3]-2*cost[i][j]-cost[R3][C3]); } } System.out.println("#"+testCase+" "+ANS); } } static void init() { ANS=Long.MAX_VALUE; for(int i=0;i<cost.length;i++) { Arrays.fill(cost[i], 0); Arrays.fill(visited[i], 0); } for(int i=1;i<=N;i++) { for(int j=1;j<=M;j++) { Arrays.fill(minCost[i][j], Integer.MAX_VALUE>>1); } } } static void dijkstra(int [] start, int bound) { PriorityQueue<int []> pq = new PriorityQueue<int[]>(new Comparator<int []>() { @Override public int compare(int[] o1, int[] o2) { return o1[2]-o2[2]; } }); //每次调用都需要初始化,不然第二个点和第三个点求最短路径时就不遍历了 for(int i=0;i<visited.length;i++) { Arrays.fill(visited[i], 0); } minCost[start[0]][start[1]][bound] = cost[start[0]][start[1]]; pq.add(new int [] {start[0],start[1],minCost[start[0]][start[1]][bound]}); while(!pq.isEmpty()) { int [] curr = pq.poll(); if(visited[curr[0]][curr[1]]==1) { continue; } visited[curr[0]][curr[1]] = 1; for(int i=0;i<4;i++) { int x = curr[0] + dir[i][0]; int y = curr[1] + dir[i][1]; if(isValid(x,y)) { if(minCost[x][y][bound]>minCost[curr[0]][curr[1]][bound]+cost[x][y]) { minCost[x][y][bound] = minCost[curr[0]][curr[1]][bound]+cost[x][y]; pq.add(new int[] {x,y,minCost[x][y][bound]}); } } } } } static boolean isValid(int x,int y) { return x>0&&x<=N&&y>0&&y<=M; } }