HDU 5572 An Easy Physics Problem

题目:点击打开链接
题意:二维平面中一位于(Ax, Ay)的点A以矢量v为方向运动,同时平面内有一半径为r 圆心为Ox Oy的圆,点A碰到圆就会反弹,问运动过程中是否能碰到点B。
分析:这题思路上并不难,写起来比较麻烦,抄的kuangbin的新板子,先判断一下直线AV是否与圆的交点个数,如果少于2个交点,判断B是否在射线AV上,如果有两个交点,求出距离A较近的那个交点P1,求出A关于QP1的对称点A1,判断B是否在线段AP1上或是否在射线P1A1上。

补充:这题不用long double也能过,用了保险一点,但是需要调一下eps,不能过大又不能过小,一般在1e-7至1e-8之间。计算几何模板很重要,精度也需要注意,代码比较长,包含注释。
代码:

#include<algorithm>
#include<iostream>
#include<fstream>
#include<complex>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<iomanip>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cctype>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
using namespace std;
#define pt(a) cout<<a<<endl
#define debug test
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pii pair<int,int>
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define ld long double

const ld eps = 1e-10;
const ld inf = 1e20;
const ld pi = acos(-1.0);
const int maxp = 1010;

int sgn(ld x){
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}

struct Point{
    ld x,y;
    Point(){}
    Point(ld _x,ld _y){
        x = _x;
        y = _y;
    }
    Point operator - (const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    //叉积
    ld operator ^(const Point &b)const{
        return x*b.y - y*b.x;
    }
    //点积
    ld operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
    //返回长度
    ld len(){
        return hypot(x,y);//库函数
    }
    //返回长度的平方
    ld len2(){
        return x*x + y*y;
    }
    //返回两点的距离
    ld distance(Point p){
        return hypot(x-p.x,y-p.y);
    }
    Point operator +(const Point &b)const{
        return Point(x+b.x,y+b.y);
    }
    Point operator *(const ld &k)const{
        return Point(x*k,y*k);
    }
    Point operator /(const ld &k)const{
        return Point(x/k,y/k);
    }
    //化为长度为 r 的向量
    Point trunc(ld r){
        ld l = len();
        if(!sgn(l))return *this;
        r /= l;
        return Point(x*r,y*r);
    }
};

struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }
    //求线段长度
    ld length(){
        return s.distance(e);
    }
    //返回直线倾斜角 0<=angle<pi
    ld angle(){
        ld k = atan2(e.y-s.y,e.x-s.x);
        if(sgn(k) < 0)k += pi;
        if(sgn(k-pi) == 0)k -= pi;
        return k;
    }
    //点和直线关系
    //1 在左侧
    //2 在右侧
    //3 在直线上
    int relation(Point p){
        int c = sgn((p-s)^(e-s));
        if(c < 0)return 1;
        else if(c > 0)return 2;
        else return 3;
    }
    //点到直线的距离
    ld dispointtoline(Point p){
        return fabs((p-s)^(e-s))/length();
    }
    // 点在线段上的判断
    bool pointonseg(Point p){
        return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0;
    }
    //返回点 p 在直线上的投影
    Point lineprog(Point p){
        return s + ( ((e-s)*((e-s)*(p-s)))/((e-s).len2()) );
    }
    //返回点 p 关于直线的对称点
    Point symmetrypoint(Point p){
        Point q = lineprog(p);
        return Point(2*q.x-p.x,2*q.y-p.y);
    }
};

struct circle{
    Point p;//圆心
    ld r;//半径
    circle(Point _p,ld _r){
        p = _p;
        r = _r;
    }
    //直线和圆的关系
    //比较的是圆心到直线的距离和半径的关系
    int relationline(Line v){
        ld dst = v.dispointtoline(p);
        if(sgn(dst-r) < 0)return 2;
        else if(sgn(dst-r) == 0)return 1;
        return 0;
    }
    //求直线和圆的交点,返回交点个数
    int pointcrossline(Line v,Point &p1,Point &p2){
        if(!(*this).relationline(v))return 0;
        Point a = v.lineprog(p);
        ld d = v.dispointtoline(p);
        d = sqrt(r*r-d*d);
        if(sgn(d) == 0){
            p1 = a;
            p2 = a;
            return 1;
        }
        p1 = a + (v.e-v.s).trunc(d);
        p2 = a - (v.e-v.s).trunc(d);
        return 2;
    }
};

int t,r;
Point Q,A,B,V;
int main() {
    cin>>t;
    for(int cas=1;cas<=t;cas++) {
        cin>>Q.x>>Q.y>>r;
        cin>>A.x>>A.y>>V.x>>V.y;
        cin>>B.x>>B.y;
        V=Point(A.x+V.x,A.y+V.y);
        Line LA=Line(A,V);
        circle O = circle(Q,r);
        Point P1,P2;
        int cnt = O.pointcrossline(LA,P1,P2);
        int f=0;
        if(cnt==0||cnt==1) {
            ///如果圆和向量(直线)AV少于两个交点,直接判断点B是否在射线AV上
            if(LA.relation(B) == 3 && sgn((V-A)*(B-A)) > 0 ) f=1;
        }else {
            ///否则先判断点B是否在线段AP1上,再判断是否在射线P1A1上,A1为点A关于直线QP1(圆的半径)的对称点
            if(sgn(A.distance(P1)-A.distance(P2)) >0 ) P1=P2;///P1为第一个交点(较近的点)
            Line QP1=Line(Q,P1);
            Line AP1=Line(A,P1);
            Point A1=QP1.symmetrypoint(A);
            if( AP1.pointonseg(B)) f=1;
            Line P1A1=Line(P1,A1);
            if( P1A1.relation(B) == 3 && sgn((A1-P1)*(B-P1)) > 0 ) f=1;
        }
        cout<<"Case #"<<cas<<": ";
        if(f) cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值