Problem:
Problem C: Dragon Fantasy

永遠に続くと思われていた平和は突然終わりを告げた。 はるか昔に封印されていた魔王がついに復活を遂げたのである。 だが、今まさに世界が闇に覆われようとしているとき、1 人の勇者が現れた。 そしてその勇者は、世界各地に散らばっている伝説のクリスタルを集める旅に出た。 伝説によると、全てのクリスタルを集めることができたならば、どのような願いでもかなえられるという伝説の龍神を呼び出すことができると伝えられている。 その龍神の力を借りれば魔王を倒すことも可能なはずだ。

クリスタルは世界各地に散らばっている。 勇者はそれらを 1 つずつ、自らの手で集めていく必要がある。 なぜなら、勇者以外の者がクリスタルを手に入れたならば、魔王の手先によって奪われてしまうかもしれないからである。 勇者は、1 日あたりユークリッド距離で 1 だけ移動することができる。

しかし、ここで 1 つの重大な問題がある。 魔王は、常に闇の瘴気をまき散らしており、その瘴気に汚染された場所は人が立ち入ることのできない死の大地と化してしまう。 たとえ勇者であってもその場所に立ち入ることはできない。 さらに、その瘴気は時間とともに同心円状に広まっていくため、 時間の経過とともに勇者の移動できる領域は減少していってしまう。 瘴気は 1 日あたりユークリッド距離 1 だけ広がることが確認されている。 そして、その境界線上にあるクリスタルを勇者は取ることはできない。また、復活したばかりの魔王は力を蓄えるために動かないことも分かっている。

勇者は一刻も早くクリスタルを手に入れなければならない。 しかし、もし全てのクリスタルを手に入れることが不可能であるならば、別の手段を考えなければならないだろう。 そこで、あなたには勇者の初期位置と魔王の復活した場所、そしてクリスタルの存在する位置から、全てのクリスタルを手に入れることができるかどうかを調べるプログラムを作っていただきたい。

ちなみに、勇者は疲れない。 そして眠らない。 全てのクリスタルを集めて世界を救うまでは絶えず動き続け、クリスタルを集めるのだ！！

Input

入力は複数のテストケースから構成される。 各テストケースの先頭の行では 5 つの整数 n (0 < n <= 20)、hx 、hy 、dx 、dy が与えられる。
n はクリスタルの個数、(hx , hy ) は魔王が復活した瞬間での勇者の位置、(dx , dy ) は魔王が復活した位置である。
後に続く n 行には、各クリスタルの位置を表す 2 つの整数 cx 、cy が与えられる。 入力は n = hx = hy = dx = dy = 0 のときに終了し、これはテストケースには含まない。

入力で与えられる全ての座標は絶対値が 1000 以下の整数であることが保証されている。

Output

各テストケースについて、全てのクリスタルを集めることができるならば「YES」と、そうでないならば「NO」と出力せよ。

Sample Input

2 0 0 10 10
1 1
4 4
2 0 0 10 10
1 1
6 6
2 0 0 10 10
1 1
5 5
0 0 0 0 0

Output for the Sample Input

YES
NO
NO

Solution in JAVA (judge verdict: Accepted)

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.PriorityQueue;
import java.util.Scanner;
    
public class Main {
        
    public static final int MAX = 20;
    public static final double INF = Double.MAX_VALUE;
        
        
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
            
        double[][] dp = new double[1 << MAX][MAX];
        long[][] points = new long[MAX][2];
        double[][] adj = new double[MAX][MAX];
            
        while(true){
            final int n = sc.nextInt();
                
            if(n == 0){
                break;
            }
                
            final long h_x = sc.nextInt();
            final long h_y = sc.nextInt();
            final long g_x = sc.nextInt();
            final long g_y = sc.nextInt();
                
            for(int i = 0; i < n; i++){
                for(int j = 0; j < 2; j++){
                    points[i][j] = sc.nextInt();
                }
            }
                
            final int size = n;
            final int limit = 1 << size;
                
            for(int i = 0; i < size; i++){
                for(int j = i + 1; j < size; j++){
                    long sum = 0;
                    for(int k = 0; k < 2; k++){
                        sum += (points[i][k] - points[j][k]) * (points[i][k] - points[j][k]);
                    }
                        
                    adj[i][j] = adj[j][i] = Math.sqrt(sum);
                    //System.out.println(adj[i][j]);
                }
            }
                
            for(int S = 0; S < limit; S++){
                for(int i = 0; i < size; i++){
                    dp[S][i] = INF;
                }
            }
            for(int i = 0; i < n; i++){
            	final double h_time = Math.sqrt(
                        (points[i][0] - h_x) * (points[i][0] - h_x) + 
                        (points[i][1] - h_y) * (points[i][1] - h_y));
            	
            	final double g_time = Math.sqrt(
                        (points[i][0] - g_x) * (points[i][0] - g_x) + 
                        (points[i][1] - g_y) * (points[i][1] - g_y));
                
                dp[1 << i][i] = h_time < g_time ? h_time : INF; 
            }
                
            for(int S = 0; S < limit; S++){
               LOOP: 
               for(int i = 0; i < size; i++){
                    if((S & (1 << i)) == 0){
                        continue;
                    }else if(dp[S][i] == INF){
                        continue;
                    }
                        
                    for(int j = 0; j < size; j++){
                        if((S & (1 << j)) != 0){
                            continue;
                        }
                           
                        final double time = Math.sqrt(
                                (points[j][0] - g_x) * (points[j][0] - g_x) + 
                                (points[j][1] - g_y) * (points[j][1] - g_y));
                        final double cost = dp[S][i] + adj[i][j];  
                            
                        if(time <= cost){
                           continue LOOP;
                        }
                    }
                       
                    for(int j = 0; j < size; j++){
                         if((S & (1 << j)) != 0){
                            continue;
                        }
                                
                        final double cost = dp[S][i] + adj[i][j];
                            
                        dp[S | (1 << j)][j] = Math.min(dp[S | (1 << j)][j], cost);
                    }
                }
            }
                
            double min = INF;
            for(int i = 0; i < size; i++){
                //System.out.print(dp[limit - 1][i] + " ");
                min = Math.min(min, dp[limit - 1][i]);
            }
                
            //System.out.println(min);
                
            System.out.println(min == INF ? "NO" : "YES");
        }
    }
    
}