题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5572
题意:给一个圆和圆外两点A、B,在A以给定的速度(方向)出发,若碰到圆则发生完全弹性碰撞,问能否经过B。
这题现场赛时卡出了xiang了~,并最终导致打铁。
解法:
圆心O(x0,y0) , A(x1,y1) , B(x2,y2) , Vec(v1,v2)
A点在碰撞到圆之前的运动参数方程可以确定
x=x1+t*v1
y=y1+t*v2 (t>0)
将其带入圆的方程 (x-x0)*(x-x0)+(y-y0)*(y-y0)=r*r
可以得到一个 关于 t 的一元二次方程 a*t*t+b*t+c=0
解出
a=v1*v1+v2*v2
b=v1*(x1-x0)*2+v2*(y1-y0)*2
c=(x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)-r*r(第一个bug,比赛 时x1-x0 推成了 x1+x0 ,结果队友出的都是x0=0的数据,导致没发现这个错误,还是自己菜,这么简单的公式都推错)
然后做如下判断:
1,看A能否与圆接触,如不能直接判断A能否经过B。
2,若A能接触圆,用一元二次方程求出交点C的坐标,然后将向量C->A旋转得到反弹后的运动方向,进而确定参数方程,再判断能否经过B。
3,解题过程中要用long double 否则精度不够(这是现场赛没改出的第二个bug)。
下面是AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
using namespace std;
typedef long double LD;
const LD pi=acos(-1.0);
const LD eps=1e-10;
int dcmp(LD x){
if(fabs(x)<=eps) return 0;
else return x<0?-1:1;
}
struct Point
{
LD x,y;
Point(){}
Point(LD _x,LD _y){
x = _x;y = _y;
}
};
bool operator < (const Point& a,const Point& b) {
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
bool operator == (const Point& a,const Point &b) {
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
typedef Point Vec; //оРа©
Vec operator + (Vec A,Vec B) {return Vec(A.x+B.x,A.y+B.y); }
Vec operator - (Point A,Point B) {return Vec(A.x-B.x,A.y-B.y); }
Vec operator * (Vec A,LD p) {return Vec(A.x*p,A.y*p); }
Vec operator / (Vec A,LD p) {return Vec(A.x/p,A.y/p); }
LD Dot (Vec A,Vec B) { return A.x*B.x+A.y*B.y; }
LD Length(Vec A) { return sqrt(Dot(A,A)); }
LD Angle(Vec A,Vec B) { return acos(Dot(A,B)/Length(A)/Length(B)); }
LD Cross(Vec A,Vec B) { return A.x*B.y-A.y*B.x; }
Vec Rotate(Vec A,LD rad) {
return Vec(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
int main (){
/* Vec pp;
while(scanf("%lf%lf",&pp.x,&pp.y)!=EOF){
pp=Rotate(pp,pi/2);
cout<<pp.x<<" "<<pp.y<<endl;
}*/
LD v1,v2;
LD x0,y0,x1,y1,x2,y2,r;
int countt;
scanf("%d",&countt);
for(int kk=1;kk<=countt;kk++){
cin>>x0>>y0>>r;
cin>>x1>>y1>>v1>>v2;
cin>>x2>>y2;
printf("Case #%d: ",kk);
LD a=v1*v1+v2*v2;
LD b=v1*(x1-x0)*2+v2*(y1-y0)*2;
LD c=(x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)-r*r;
if(b*b-a*c*4<=0){
if(dcmp(v1)==0){
LD t=(y2-y1)/v2;
if(t>=0&&dcmp(x2-v1*t-x1)==0)
printf("Yes\n");
else
printf("No\n");
}
else{
LD t=(x2-x1)/v1;
if(t>=0&&dcmp(y2-t*v2-y1)==0)
printf("Yes\n");
else
printf("No\n");
}
continue;
}
LD t=(-b-sqrt(b*b-a*c*4))/a/2.0;
if(t<0){
if(dcmp(v1)==0){
LD tt=(y2-y1)/v2;
if(tt>=0&&dcmp(x2-v1*tt-x1)==0)
printf("Yes\n");
else
printf("No\n");
}
else{
LD tt=(x2-x1)/v1;
if(tt>=0&&dcmp(y2-tt*v2-y1)==0)
printf("Yes\n");
else
printf("No\n");
}
continue;
}
int is=0;
if(dcmp(v1)==0){
LD tt=(y2-y1)/v2;
if(tt>=0&&dcmp(x2-v1*tt-x1)==0&&tt<=t)
is=1;
}
else{
LD tt=(x2-x1)/v1;
if(tt>=0&&dcmp(y2-tt*v2-y1)==0&&tt<=t)
is=1;
}
if(is==1){
printf("Yes\n");
continue;
}
Point A,O,C,B;
A.x=x1,A.y=y1;
B.x=x2,B.y=y2;
O.x=x0,O.y=y0;
C.x=x1+v1*t,C.y=y1+v2*t;
/*if(dcmp(Length(A-C)-Length(B-C)-Length(A-B))==0){
printf("Yes\n");
continue;
}
if(dcmp(Angle(A-C,C-O)-Angle(B-C,C-O))==0&&
dcmp(Angle(A-C,B-C)-2*Angle(A-C,C-O))==0){
printf("Yes\n");
}
else
printf("No\n");*/
LD ang=Angle(C-O,A-C);
Vec dir;
if(Cross(C-O,A-C)>0)
dir=Rotate(A-C,-ang*2);
else
dir=Rotate(A-C,ang*2);
// cout<<t<<endl;
// cout<<dir.x<<" "<<dir.y<<endl;
// cout<<C.x<<" "<<C.y<<endl;
v1=dir.x,v2=dir.y;
x1=C.x,y1=C.y;
if(dcmp(v1)==0){
LD tt=(y2-y1)/v2;
if(tt>=0&&dcmp(x2-v1*tt-x1)==0)
printf("Yes\n");
else
printf("No\n");
}
else{
LD tt=(x2-x1)/v1;
if(tt>=0&&dcmp(y2-tt*v2-y1)==0)
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}