AOJのこちらの問題を綺麗に書こうと考えています。
Acceptedはもらえたのですが他に解き方は無いか教えてください(特に当たり判定)
ソースコード
► スポイラーを表示
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cassert>
#include <functional>
using namespace std;
class ufo{
public:
int x; //元のX座標
int y; //元のY座標
int r; //UFOの半径
int v; //UFOの速度
int move_length; //その時の合計移動距離
int laser; //レーザーの効かない範囲
ufo( int x_,int y_,int r_, int v_, int laser_):
x(x_),y(y_),r(r_),v(v_),move_length(v_),laser(laser_){}
bool operator < (const ufo& one) const{
return sqrt(x*x+y*y)-move_length <
sqrt(one.x*one.x + one.y*one.y) -one.move_length;
} //比較基準はその時の中心からの距離が近い順
double length() const
{
return sqrt(x*x+y*y)-move_length; //中心からの距離
}
double nowx() const//今のx座標
{
return x*(1-move_length/sqrt(x*x+y*y));
}
double nowy() const//今のY座標
{
return y*(1-move_length/sqrt(x*x+y*y));
}
bool move()//合計移動距離を分速分増やす
{
move_length += v;
return move_length+laser >= sqrt(x*x+y*y);//通りすぎてないかのチェック
}
};
bool hit(int laser,double x, double y,ufo one);
//当たり判定用関数
class can_shoot : public std::binary_function<ufo, int, int>
{
public:
bool operator() (ufo one,int laser) const
{
return one.length() > laser;
}
};//UFOが射程圏内にいるかどうかのチェック
int main(){
int laser,num;
int answer = 0;
int x,y,r,v;
vector<ufo> vec;
while(cin >> laser >> num){
if(laser == 0 && num == 0)
return 0;
answer = 0;
//入力
for(int i = 0 ; i < num; i++)
{
cin >> x >> y >> r >> v;
vec.push_back(ufo(x,y,r,v,laser));
}
//計算部分
while(!vec.empty())
{
sort(vec.begin(),vec.end()); //中心から近い順にソート
auto ite = find_if (vec.begin(), vec.end(), bind2nd(can_shoot(),laser) );
//撃てるいちばん近い標的を探す
for (auto i = vec.begin(); i!= ite; i++)
answer++; //撃てない機体分answerを増やす
ite = vec.erase(vec.begin(),ite); //撃てない機体をvectorから削除
if(vec.empty()) break; //この時点で空なら終了
double now_x = vec[0].x; //いちばん近いUFOの座標を記録
double now_y = vec[0].y; //この座標を狙ってレーザー発射
vec.erase(vec.begin()); //いちばん近いUFOをvecから削除
for(auto ite = vec.begin();ite != vec.end();)
{
if( hit(laser,now_x,now_y,*ite) ) //当たるなら
ite = vec.erase(ite); //削除
else //当たらないなら
ite++; //イテレーターを1つ進める
}
for(auto ite = vec.begin();ite != vec.end();)
{
if((*ite).move()){ //一分進めたときに原点を通り過ぎたら
ite = vec.erase(ite); //vecから削除してanswerを一増やす
answer++;
}else{ //当たらないなら
ite++; //イテレーターを1つ進める
}
}
}
cout << answer << endl; //答えを表示
}
return 0;
}
bool hit(int laser,double x, double y,ufo one)
//引数は前から順に レーザーの効かない範囲 標的となっているUFOのx座標、y座標 当たるか判定をするUFO
{ //レーザーが効く点のうち一番原点に近い点をPとおく
double laser_x = x*laser/sqrt(x*x+y*y); //Pのx座標
double laser_y = y*laser/sqrt(x*x+y*y); //Pのy座標
if( one.r //UFOの半径が UFOの位置からPまでの距離より長ければ当たる
>= sqrt( pow(laser_x-one.nowx() , 2.0)
+pow(laser_y-one.nowy() , 2.0) ))
return 1;
if(y == one.nowy()) //標的UFOと判定UFOのy座標が同じなら
{
double kouten = //Pを通りレーザーに垂直な直線と標的UFOの中心と判定UFOの中心を結んだ直線の交点のx座標
(y*( y*one.nowx()-x*one.nowy() ) + (x-one.nowx())*(x*laser_x+y*laser_y))
/
(y*y+x*x-y*one.nowy()-x*one.nowx());
if((x <= kouten && kouten <= one.nowx()) || //交点が標的UFOと判定UFOの間にある
(one.nowx() <= kouten && kouten <= x))
return 0; //当たらない
}else{
double kouten = //Pを通りレーザーに垂直な直線と標的UFOの中心と判定UFOの中心を結んだ直線の交点のy座標
(x*( x*one.nowy()-y*one.nowx() ) + (y-one.nowy())*(x*laser_x+y*laser_y))
/
(x*x+y*y-x*one.nowx()-y*one.nowy());
if((y <= kouten && kouten <= one.nowy()) || //交点が標的UFOと判定UFOの間にある
(one.nowy() <= kouten && kouten <= y))
return 0; //当たらない
}
double result;
result = (y*one.nowx()-x*one.nowy()) / sqrt(x*x+y*y); //判定UFOの中心から直線レーザーにおろした垂線の長さ
if(result < 0)
result *= -1; //絶対値が付いているので-なら+に補正
return one.r >= result; //円の半径が垂線の長さより長ければ当たる
}
中央赤丸 :レーザー無効範囲
色つき円 :標的UFO
色なし緑円 :判定UFO
中央から延びる直線:レーザー 点線部は無効範囲 実線部は有効範囲
当たり判定は
①判定UFOからPまでの距離が半径より長ければtrue

②判定UFOから引いた垂線の足がレーザーの有効範囲にいるかのチェック
判定UFOの中心と標的UFOの中心を結んだ直線と
レーザーと赤円の交点を通り、レーザーに垂直な直線の交点(黄色丸)が
判定UFOと標的UFOの間にあればfalse

間に無ければ範囲内にいることだけは確定(true、falseはまだ判断つかない)

③判定UFOの中心からレーザーにおろした垂線の長さが半径より短ければtrue長ければfalse

としています。
正直マジックナンバーだらけで気持ち悪くって…
どうすればきれいに書けますか?
また、他にどんなとき方があるのか教えてください。