AOJ1309 The Two Men of the Japanese Alps

問題リンク The Two Men of the Japanese Alps

  • 概要

N本の線分で表された山の稜線がある。山の左端と右端にそれぞれ人がいる。彼らは、常にお互いのy座標が等しくなるように線分上を移動する。彼らが出会うために必要な移動距離の最小値を求めよ。
2 <= N <= 100
0 <= x, y < 1000
x_i < x_(i+1)
山の右端と左端のy座標は0

  • 解法

y座標をリストアップします。線分の途中に、このy座標に対応する点を増やします。2人は常にこの点間を移動すると考えて、ダイクストラで解きました。
この方法で頂点を増やすと頂点数が5000近くになり、状態数が5000^2となってMLEになります。しかし実際は、2人のx座標がx1 <= x2を満たす位置関係だけ考えればよいことや、同じy座標にいなければならないなどの制約から、状態数は5000^2よりかなり小さいことが予想できます。なので、2次元配列でなく、mapを使えば解決します。
状態の遷移は2人がそれぞれ、1つ後ろの頂点に動く、そのまま、1つ前の頂点に動く、の3パターンがあるため9パターンの遷移があります。

  • ソース
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;

//The Two Men of the Japanese Alps
public class AOJ1309 {

	int INF = 1<<29;
	Map<Integer, Double>[] map;
	
	double calc(double x1, double y1, double x2, double y2){
		return Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
	}
	
	@SuppressWarnings("unchecked")
	void run(){
		Scanner sc = new Scanner(System.in);
		int[] xs = new int[100], ys = new int[100], y = new int[5000];
		double[] x = new double[5000];
		for(;;){
			int n = sc.nextInt();
			if(n==0)break;
			Set<Integer> yset = new HashSet<Integer>();
			for(int i=0;i<n;i++){
				xs[i] = sc.nextInt();
				yset.add(ys[i]=sc.nextInt());
			}
			int[] Y = new int[yset.size()];
			int id = 0;
			for(int yy:yset)Y[id++]=yy;
			Arrays.sort(Y);
			id = 0;
			for(int i=0;i+1<n;i++){
				if(ys[i]==ys[i+1]){
					x[id] = xs[i];
					y[id++] = ys[i];
				}
				else if(ys[i]<ys[i+1]){
					for(int j=0;j<Y.length&&Y[j]<ys[i+1];j++){
						if(Y[j]<ys[i])continue;
						x[id] = (xs[i+1]-xs[i])*(Y[j]-ys[i]+0.)/(ys[i+1]-ys[i]) + xs[i];
						y[id++] = Y[j];
					}
				}
				else{
					for(int j=Y.length-1;j>=0&&Y[j]>ys[i+1];j--){
						if(Y[j]>ys[i])continue;
						x[id] = (xs[i+1]-xs[i])*(Y[j]-ys[i]+0.)/(ys[i+1]-ys[i]) + xs[i];
						y[id++] = Y[j];
					}
				}
			}
			x[id] = xs[n-1];
			y[id++] = 0;
			map = new Map[id];
			for(int i=0;i<id;i++)map[i]=new HashMap<Integer, Double>();
			map[0].put(id-1, 0.);
			PriorityQueue<int[]> q = new PriorityQueue<int[]>(id, new Comparator<int[]>() {
				public int compare(int[] o1, int[] o2) {
					return (int)Math.signum(map[o1[0]].get(o1[1]) - map[o2[0]].get(o2[1]));
				}
			});
			q.add(new int[]{0, id-1});
			double res = INF;
			while(!q.isEmpty()){
				int[] V = q.poll();
				int i = V[0], j = V[1];
				if(i==j){
					res = map[i].get(j); break;
				}
				for(int di=-1;di<=1;di++){
					int ni = i+di;
					if(ni<0 || id<=ni)continue;
					for(int dj=-1;dj<=1;dj++){
						int nj = j+dj;
						if(nj<0 || id<=nj || nj < ni || y[ni]!=y[nj])continue;
						double w = map[i].get(j) + calc(x[i], y[i], x[ni], y[ni]) + calc(x[j], y[j], x[nj], y[nj]);
						if(!map[ni].containsKey(nj) || w < map[ni].get(nj)){
							map[ni].put(nj, w);
							q.add(new int[]{ni, nj});
						}
					}
				}
			}
			System.out.println(res);
		}
	}
	
	public static void main(String[] args) {
		new AOJ1309().run();
	}
}