Description
gameboy是一个CS高手,他最喜欢的就是扮演警察,手持M4爆土匪的头。也许这里有人没玩过CS,有必要介绍一下“爆头”这个术语:所谓爆头,就是子弹直接命中对方的头部,以秒杀敌人。
现在用一个三维的直角坐标系来描述游戏中的三维空间(水平面为xoy平面,z轴正方向是上方)。假设游戏中角色的头是一个标准的球。告诉你土匪的身高,头部半径,所站位置的坐标;gameboy所控警察的身高,头部半径,所站位置的坐标,以及枪头所指方向的单位向量。gameboy所控警察所握的是M4,抢瞄准时枪膛中的子弹跟视线基本同线,我们忽略它们的距离,就当成同线。由于土匪手持AK47,所以他是很嚣张地正立着。而警察手持M4,正在瞄准,由于瞄准时身体微弯,视线从头心出发,他头部的实际高度比正立时低10%。
你的任务就是,计算gameboy在这一刻扣下扳机,能否爆土匪的头。注意:这里忽略子弹的直径和重力作用,也就是说子弹是无限小的,弹道是一条笔直的射线,警察与土匪间没有障碍物。并且只要子弹擦到头部,哪怕是边缘,也算爆头。
Input
测试数据的第一行有一个正整数T,表示有T组测试数据。每组数据的第一行有五个实数,h1,r1,x1,y1,z1,分别表示土匪的身高,头部半径以及所站的位置。第二行有八个实数,h2,r2,x2,y2,z2,x3,y3,z3,分别表示警察的身高,头部半径,所站位置,以及枪头所指方向的方向向量。
Output
每一组输入数据对应一行输出。如果能爆土匪的头,输出”YES”,否则输出”NO”。
Sample Input
2
1.62 0.1 10.0 10.0 10.0
1.80 0.09 0.0 0.0 0.0 1.0 1.0 1.0
1.62 0.1 0.0 0.0 0.0
1.80 0.09 10.0 10.0 10.0 -1.0 -1.0 -1.0
Sample Output
YES
YES
解题思路:假设人的头心坐标和土匪的头心坐标连接形成的向量为a,枪的向量为b,只需要判断a,b向量所形成的直角三角形的另一个直角边是否小于等于土匪的头部半径,如果是则能爆头,不是则不能。这里用到了一个简单的数学公式。向量a叉乘向量b等于a的模乘b的模乘sin(a,b),假设h为所求的另一个直角边,那么h=(|a叉乘b|)/|b|。这时候只需要比较h与土匪头部半径即可………这时候还没有结束,因为枪可以不指向土匪的方向,所以还要判断向量b与a的夹角。
还有一些小细节需要注意,因为是三维的,所以人可以站在空中,另外警察的身高还需乘以0.9(他头部的实际高度比正立时低10%)。所以警察在z轴的距离应该是(h2*0.9-r2+z2),土匪在z轴的距离应该是h1-r1+z1。
#include<iostream>
#include<cmath>
using namespace std;
double cross(double a,double b,double c,double x,double y,double z)
{
double t=(b*z-c*y)*(b*z-c*y)+(c*x-a*z)*(c*x-a*z)+(a*y-b*x)*(a*y-b*x);
double t2=x*x+y*y+z*z;
return sqrt(t/t2);
}//这个函数用来计算h
int main()
{
int T;
cin>>T;
while(T--)
{
double h1,r1,x1,y1,z1;
double h2,r2,x2,y2,z2,x3,y3,z3;
cin>>h1>>r1>>x1>>y1>>z1;
cin>>h2>>r2>>x2>>y2>>z2>>x3>>y3>>z3;
z2=h2*0.9-r2+z2; //计算警察在z轴的距离。
z1=h1-r1+z1; //计算土匪在z轴的距离
double x,y,z;
x=x1-x2;
y=y1-y2;
z=z1-z2; //这三个为警察头心和土匪头心形成的向量
double t=cross(x,y,z,x3,y3,z3);
int flag;
flag=x*x3+y*y3+z*z3;//判断向量a和向量b所形成的夹角是否小于90度。
if(t<=r1&&flag>0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}