暑假 - 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);
}
}
}
浙公网安备 33010602011771号