AOJ0204 UFO Shooting Down Operation

問題リンク UFO Shooting Down Operation

  • 解法

幾何+シミュレーション問題です。
まず、準備立てとして次のものを求めておきます。
1. ufoの位置(x, y)
2. 1分毎のufoの移動量ベクトル
3. 原点からの距離
自分のソースの場合、1はufo[i][0]とufo[i][1]にそれぞれx,yが、2はadv[i][0], adv[i][1]に入っています。移動量ベクトルはUFOの初期位置ベクトルを逆にし正規化する(ベクトルの大きさでx成分、y成分を割る)ことで求めます。3は初期位置の距離から毎分UFOの分速を引くことで簡単に求められます。
シミュレーション部分の説明をします。
まず、Rより大きい距離離れているUFOの中で最も近い位置にいるUFOを見つけます。このとき、R以下の距離に入っているUFOはレーザーの当たらない範囲内に逃げおおせたことになるので、カウントし、そのUFOに今後考慮しない印をつけておきます。また、Rより離れているUFOがいない場合はシミュレーション終了です。
レーザーを射出するUFOが決まったら、レーザーの方向ベクトルPが定まります。(ufo[i][0], ufo[i][1])というベクトルがレーザーの方向ベクトルです。これは正規化しておきます。
さて、射出する方向にいるUFOは撃墜できると分かりますが、このレーザーによって別のUFOも撃墜できるかチェックしないといけません。チェックしたいUFOをiとします。まず、UFO i の中心の位置ベクトルをCとし、半径をrとします。このとき、PとCとrを使って以下の図のようなtの2次方程式が導けます。

このtは、レーザーとUFOが衝突する場所t1, t2を求める式となります。つまり、このt1、t2のうち、いずれかがRより大きければ、レーザーの効かない範囲外でレーザーを当てることができることになり、UFO i を撃墜することができます。

  • ソース
import java.util.Scanner;

//UFO Shooting Down Operation
public class AOJ0204 {

	double dot(double[] a, double[] b){
		return a[0]*b[0]+a[1]*b[1];
	}
	
	void run(){
		Scanner sc = new Scanner(System.in);
		while(true){
			int R = sc.nextInt();
			int n = sc.nextInt();
			if((R|n)==0)break;
			double[][] ufo = new double[n][4];
			for(int i=0;i<n;i++)for(int j=0;j<4;j++)ufo[i][j]=sc.nextDouble();
			double[][] adv = new double[n][2];
			double[] dist = new double[n];
			for(int i=0;i<n;i++){
				double norm = Math.hypot(ufo[i][0], ufo[i][1]);
				dist[i] = norm-ufo[i][3];
				adv[i][0] = -ufo[i][0]/norm*ufo[i][3];
				adv[i][1] = -ufo[i][1]/norm*ufo[i][3];
				ufo[i][0] += adv[i][0];
				ufo[i][1] += adv[i][1];
			}
			boolean[] u = new boolean[n];
			int c = 0;
			while(true){
				double min = 1<<20;
				int id = -1;
				for(int i=0;i<n;i++){
					if(u[i])continue;
					double d = dist[i];
					if(d<=R){
						u[i] = true;
						c++;
					}
					else if(d<min){
						min = d;
						id = i;
					}
				}
				if(id==-1)break;
				double norm = Math.hypot(ufo[id][0], ufo[id][1]);
				double[] p = {ufo[id][0]/norm, ufo[id][1]/norm};
				for(int i=0;i<n;i++){
					if(u[i])continue;
					double[] ci = {ufo[i][0], ufo[i][1]};
					double A = dot(p, p);
					double B = -2*dot(p, ci);
					double C = dot(ci, ci)-ufo[i][2]*ufo[i][2];
					double D = B*B-4*A*C;
					if(D>=0){
						double t1 = (-B-Math.sqrt(D))/(2*A);
						double t2 = (-B+Math.sqrt(D))/(2*A);
						if(t1>R||t2>R)u[i] = true;
					}
					ufo[i][0] += adv[i][0];
					ufo[i][1] += adv[i][1];
					dist[i] -= ufo[i][3];
				}
			}
			System.out.println(c);
		}
	}
	
	public static void main(String[] args) {
		new AOJ0204().run();
	}
}