AOJ1318 Long Distance Taxi

問題リンク Long Distance Taxi

  • 概要

ガソリンが最大capまで積めるタクシーがある。タクシーはガソリン1毎に10km走ることができる。タクシーが町SからTまでガス欠を起こすことなく走るとき、その最短距離を答えよ。辿りつけない場合は-1とせよ。
道路がN本ある。2つの町を結んでおり、その距離はDkmである。両端の町が同じ町であったり、町と町の間に道が2本以上あることはない。また、M個の町にはガソリンスタンドがあり、ガソリンを最大まで補給することができる。最初ガソリンは最大まで入っている。また、町に辿りついた瞬間にガソリンが無くなった場合は、ガス欠を起こす前に辿りついたと考えてよい。
1 <= N <= 3000
1 <= M <= 300
1 <= cap <= 200
0 < D <= 2000

  • 解法

ダイクストラで解きます。
(町、ガソリンの残量)のノードがまず思い付くと思いますが、残量が小数点を含むことになり嬉しくありません。ガソリン1で10km走れるので、cap*10 km走れることになり、(町、後何km走れるか)のノードにすることで整数のみの形に落とせます。あとは、ダイクストラを書くだけです。注意すべきは、Nは道路の数であり、町の数ではないということです。町の数は最大で6000まであることになります。
書いたプログラムがMLEではじかれるかもしれません。プログラムでダイクストラのノードを表す配列をデータセット毎に取っていたら、それはやめましょう。一番最初に最大サイズである6000*2001の配列を取得し、データセット毎に利用する領域だけ初期化する、ということをするといいと思います、実行時間もかなり早くなるでしょう。
TLEがでた場合で、PriorityQueueにノードを渡す時に配列をnew して渡していたとしたら、配列の要素を1つの整数値にして渡すと速くなると思います。配列をnew する処理は意外と重いので。

  • ソース
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Scanner;
import java.util.Set;

//Long Distance Taxi
public class AOJ1318 {

	class E{
		int t, c;
		public E(int t, int c) {
			this.t = t;
			this.c = c;
		}
	}
	
	Map<String, Integer> ref;
	int ID, INF = 1<<29, M = 2001;
	int reg(String s){
		if(ref.containsKey(s))return ref.get(s);
		ref.put(s, ID);
		return ID++;
	}

	Set<E>[] adj;
	int[] d;
	boolean[] gas;
	
	@SuppressWarnings("unchecked")
	void run(){
		Scanner sc = new Scanner(System.in);
		d = new int[6000*2001];
		adj = new Set[6000];
		gas = new boolean[6000];
		for(;;){
			int n = sc.nextInt(), m = sc.nextInt(), cap = sc.nextInt();
			if((n|m|cap)==0)break;
			ref = new HashMap<String, Integer>();
			ID = 0;
			int S = reg(sc.next()), T = reg(sc.next());
			for(int i=0;i<6000;i++)adj[i]=new HashSet<E>();
			while(n--!=0){
				int s = reg(sc.next()), t = reg(sc.next()), D = sc.nextInt();
				adj[s].add(new E(t, D)); adj[t].add(new E(s, D));
			}
			Arrays.fill(gas, false);
			while(m--!=0)gas[reg(sc.next())]=true;
			for(int i=0;i<ID;i++)for(int j=0;j<=10*cap;j++)d[i*M+j]=INF;
			PriorityQueue<Integer> q = new PriorityQueue<Integer>(ID, new Comparator<Integer>() {
				public int compare(Integer o1, Integer o2) {
					return d[o1]-d[o2];
				}
			});
			d[S*M+10*cap] = 0;
			q.add(S*M+10*cap);
			int res = -1;
			while(!q.isEmpty()){
				int v = q.poll();
				int p = v/M, c = v%M;
				if(p==T){
					res = d[v]; break;
				}
				for(E e:adj[p]){
					if(c-e.c<0)continue;
					int w = d[v]+e.c;
					if(gas[e.t]){
						if(w < d[e.t*M+10*cap]){
							d[e.t*M+10*cap] = w; q.add(e.t*M+10*cap);
						}
					}
					else{
						if(w < d[e.t*M+c-e.c]){
							d[e.t*M+c-e.c] = w; q.add(e.t*M+c-e.c);
						}
					}
				}
			}
			System.out.println(res);
		}
	}
	
	public static void main(String[] args) {
		new AOJ1318().run();
	}
}