暑假 - Summer Vacation - Dijkstra & DP

暑假

时限:最多 X 个用例,1.5 秒 (C/C++),2 秒 (Java)

 

假设有个国家有 N 座城市,通过 M 条道路连通,城市从 1 到 N 进行编号,每条道路上都会收取过路费。

据说,这个国家每年的暑期会向全体市民发放两张过路费折扣券(每张券的折扣力度可能会不同)。John 住在 1 号城市,准备前往 N 号城市度假,想要使用折扣券确保支付最少的过路费

 

暑假 - Summer Vacation - Dijkstra & DP

 

[图]

如上 [图] 所示,假设从 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);
		}
	}
}

  

上一篇:Dijkstra算法模板


下一篇:狼蛛 - Tarantula - Dijkstra