暑假 - Summer Vacation - Dijkstra & DP
暑假
时限:最多 X 个用例,1.5 秒 (C/C++),2 秒 (Java)
假设有个国家有 N 座城市,通过 M 条道路连通,城市从 1 到 N 进行编号,每条道路上都会收取过路费。
据说,这个国家每年的暑期会向全体市民发放两张过路费折扣券(每张券的折扣力度可能会不同)。John 住在 1 号城市,准备前往 N 号城市度假,想要使用折扣券确保支付最少的过路费。
[图]
如上 [图] 所示,假设从 1 号城市出发,前往 8 号城市,收到的折扣券金额分别是 2 和 10。
在路径 1 → 2 → 4 → 5 → 6 → 8、路径 1 → 2 → 4 → 6 → 8、以及路径 1 → 5 → 8 中,如果选择路径 1 → 2 → 4 → 5 → 6 → 8,不使用折扣券的情况总费用是 17,但如果使用折扣券,到 8 号城市的总费用是 9。(从 6 号城市到 8 号城市时,使用金额为 10 的折扣券,在剩余其中一条道路上使用金额为 2 的折扣券。)
如果选择路径 1 → 2 → 4 → 6 → 8,不使用折扣券的情况总费用是 19,但如果使用折扣券,到 8 号城市的总费用是 9。(从 4 号城市到 6 号城市时,使用金额为 10 的折扣券,在剩余其中一条道路上使用金额为 2 的折扣券。)
如果选择路径 1 → 5 → 8,不使用折扣券的情况需要花费 18,如果使用折扣券,到 8 号城市的总费用就是 6,这也是能实现的最小总费用。(从 5 号城市到 8 号城市时,使用金额为 10 的折扣券,从 1 城市到 5 号城市时,使用金额为 2 的折扣券。)
请帮助 John 计算出在暑期使用折扣券从 1 号城市前往 N 号城市度假要支付的最少过路费。
[限制条件]
1.城市数量 N 为介于 2 到 40,000 之间的整数。
2.道路数量 M 为介于 N-1 到 100,000 之间的整数。
3.道路的过路费 M 为介于 1 到 100,000 之间的整数。
4.折扣券的折扣金额 P 为介于 1 到 100,000 之间的整数。
5.每张折扣券只能使用一次,一条道路上不能同时使用两张折扣券。
6.如果使用的折扣券金额比该道路的过路费更高,路过该道路需支付的过路费为 0。
[输入]
在第一行,给定测试用例数量 T。在每个测试用例的第一行,给定城市数量 N、道路数量 M,以空格分隔。接下来的一行,给定两张折扣券的金额,以空格分隔。接下来从第三行起到后面的 M 行,每行给定一条道路的信息(道路两端的城市编号 A 和 B,以及道路的过路费 C),格式为 A B C,三者以空格分隔。
[输出]
每行输出一个测试用例。每个测试用例,都输出“#x”(其中 x 表示测试用例编号,从 1 开始),然后是使用折扣券在暑期前往目的地的过程中要支付的最少费用。
[输入和输出示例]
(输入)
2
8 13
2 10
1 2 3
1 5 7
2 4 2
3 4 3
3 6 11
3 7 15
4 5 2
4 6 8
5 6 4
5 8 11
6 7 2
6 8 6
7 8 4
6 8
3 20
1 2 3
4 5 3
5 6 3
3 1 5
2 3 3
6 1 15
3 4 3
3 6 7
(输出)
#1 6
#2 0
解题思路:
求最短路径,用Dijkstra ;折扣与否只与前一个状态有关,动态规划思想
三维dp[i][p1][p2]: i -> 节点编号
p1-> 折扣1使用与否,0:不使用,1:使用
p2-> 折扣1使用与否,0:不使用,1:使用
package pro.dijkstra; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.PriorityQueue; import java.util.StringTokenizer; /** * 求最短路径,用Dijkstra * 折扣与否,只与前一个状态有关,动态规划思想 * 三维dp[i][p1][p2]: i -> 节点编号 * p1-> 折扣1使用与否,0:不使用,1:使用 * p2-> 折扣1使用与否,0:不使用,1:使用 * * @author XA-GDD * */ public class SummerVacation_0813 { static int T,N,M; static long [][][] minCost; static boolean [][][] visited; static int ticket1 ,ticket2; static ArrayList<int[]> [] adjList; //邻接链表 static long ANS; public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\sample_input_0813.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()); minCost = new long[N+1][2][2]; visited = new boolean[N+1][2][2]; st = new StringTokenizer(br.readLine()); ticket1 = Integer.parseInt(st.nextToken()); ticket2 = Integer.parseInt(st.nextToken()); adjList = new ArrayList[N+1]; for(int i=0;i<=N;i++) { adjList[i] = new ArrayList<int[]>(); } for(int i=0;i<M;i++) { st = new StringTokenizer(br.readLine()); int start = Integer.parseInt(st.nextToken()); int end = Integer.parseInt(st.nextToken()); int cost = Integer.parseInt(st.nextToken()); adjList[start].add(new int[] {end,cost}); adjList[end].add(new int[] {start,cost}); } dijkstra(); ANS = Long.MAX_VALUE; for(int i=0;i<2;i++) { for(int j=0;j<2;j++) { ANS = Math.min(minCost[N][i][j], ANS); } } System.out.printf("#%d %d\n",testCase,ANS); } } static void dijkstra() { PriorityQueue<CityNode> pq = new PriorityQueue<CityNode>(); for(int i=2;i<=N;i++) { minCost[i][0][0] = minCost[i][0][1] = minCost[i][1][0] = minCost[i][1][1] = Long.MAX_VALUE/2; } pq.add(new CityNode(1)); CityNode curr; while(!pq.isEmpty()) { curr = pq.poll(); if(curr.index==N) { break; } if(visited[curr.index][curr.tck1][curr.tck2]) { continue; } visited[curr.index][curr.tck1][curr.tck2]=true; for(int i=0;i<adjList[curr.index].size();i++) { int [] next = adjList[curr.index].get(i); //不使用折扣,折扣1与折扣2还是延续之前的状态 if(minCost[next[0]][curr.tck1][curr.tck2] > curr.cost + next[1]) { minCost[next[0]][curr.tck1][curr.tck2] = curr.cost + next[1] ; pq.add(new CityNode(next[0],curr.tck1,curr.tck2,minCost[next[0]][curr.tck1][curr.tck2])); } //使用折扣1,之前必须没有用过折扣1 int cost1 = next[1]-ticket1>0?next[1]-ticket1:0; if(curr.tck1==0 && minCost[next[0]][1][curr.tck2] > curr.cost + cost1) { minCost[next[0]][1][curr.tck2] = curr.cost + cost1; pq.add(new CityNode(next[0],1,curr.tck2,minCost[next[0]][1][curr.tck2])); } //使用折扣2 int cost2 = next[1]-ticket2>0?next[1]-ticket2:0; if(curr.tck2==0 && minCost[next[0]][curr.tck1][1] > curr.cost + cost2) { minCost[next[0]][curr.tck1][1] = curr.cost + cost2; pq.add(new CityNode(next[0],curr.tck1,1,minCost[next[0]][curr.tck1][1])); } } } } static class CityNode implements Comparable<CityNode>{ int index; //节点编号 int tck1; //折扣1是否使用,0:未使用,1:使用 int tck2; //折扣2是否使用,0:未使用,1:使用 long cost; //花费 public CityNode(int index){ this.index = index; } public CityNode(int index,int tck1, int tck2, long cost){ this.index = index; this.tck1 = tck1; this.tck2 = tck2; this.cost = cost; } @Override public int compareTo(CityNode node) { return Long.compare(this.cost, node.cost); } } }