HDU 5572 2015 上海区域赛 A题 计算几何(碰撞反弹、注意精度)

题目链接: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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值