2015 ICPC 上海站 A - An Easy Physics Problem(计算几何)

思路

分两种情况
1、不发生碰撞
2、发生碰撞

只要写出点A的射线方程,圆的方程,联立即可求出碰撞时间t的二次方程。
通过二次方程的 delta 与 0 的大小关系,判断是否碰撞。

1、不发生碰撞时
直接看点B是否在A的射线上
2、发生碰撞时
先看点 B 是否在A到碰撞点上(入射线),然后再看是否在出射线上,因为求出射线比较麻烦,这里是算出切线方程,算出点B关于切线的对称点,然后判断点B是否在A射线上。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-8;
int sgn(double x){
    if(fabs(x) < eps)
        return 0;
    if(x > 0.0)
        return 1;
    return -1;
}
int main(){
    int T, Case = 1; scanf("%d", &T);
    while(T--){
        double Ox, Oy, r, Ax, Ay, Vx, Vy, Bx, By;
        scanf("%lf %lf %lf", &Ox, &Oy, &r);
        scanf("%lf %lf %lf %lf", &Ax, &Ay, &Vx, &Vy);
        scanf("%lf %lf", &Bx, &By);

        printf("Case #%d: ", Case++);
        double a, b, c;
        a = Vx * Vx + Vy * Vy;
        b = 2.0 * ( Vx * (Ax - Ox) + Vy * (Ay - Oy));
        c = (Ax - Ox) * (Ax - Ox) + (Ay - Oy) * (Ay - Oy) - r * r;

        double delta = b * b - 4 * a * c;
        bool flag;
        double t1, t2;
        if(sgn(delta) <= 0){///小于等于,无碰撞,直接看射线
            if(sgn(Vx) != 0){
                t1 = (Bx - Ax) / Vx;
                if(sgn(By - Ay - Vy * t1) == 0){
                    flag = true;
                }else{
                    flag = false;
                }
            }
            else if(sgn(Vy) != 0){
                t2 = (By - Ay) / Vy;
                if(sgn(Bx - Ax - Vx * t2) == 0){
                    flag = true;
                }else{
                    flag = false;
                }
            }
        }else{///大于
            double t1 = (-b - sqrt(delta)) / (2.0 * a), t2;
            if(t1 < 0.0){///碰撞点在A点后面,不会碰撞,只看射线。
                if(sgn(Vx) != 0){
                    t1 = (Bx - Ax) / Vx;
                    if(sgn(By - Ay - Vy * t1) == 0){
                        flag = true;
                    }else{
                        flag = false;
                    }
                }else if(sgn(Vy) != 0){
                    t2 = (By - Ay) / Vy;
                    if(sgn(Bx - Ax - Vx * t2) == 0){
                        flag = true;
                    }else{
                        flag = false;
                    }
                }
            }else{///求解碰撞点的坐标,新的向量。(由于不会求新的向量,所以直接算出碰撞点的切线,然后将B点关于切线对称,看对称点是否在碰撞点的射线上即可)
                double hx = Ax + t1 * Vx, hy = Ay + t1 * Vy;///碰撞坐标
                double A, B, C;///切线方程
                A = hx - Ox;
                B = hy - Oy;
                C = Ox * (Ox - hx) + Oy * (Oy - hy) - r * r;
                //double t1 = (-b - sqrt(delta)) / (2.0 * a);
                ///看点B是否在入射线上
                if(sgn(Vx) != 0){
                    double t = (Bx - Ax) / Vx;
                    if(sgn(By - Ay - Vy * t) == 0 && sgn(t - t1) <= 0){
                        printf("Yes\n");
                        continue;
                    }else{
                        flag = false;
                    }
                }else if(sgn(Vy) != 0){
                    double t = (By - Ay) / Vy;
                    if(sgn(Bx - Ax - Vx * t) == 0 && sgn(t - t1) <= 0){
                        printf("Yes\n");
                        continue;
                    }else{
                        flag = false;
                    }
                }
                ///求点B关于切线方程的对称点
                double Bxx = Bx - (2.0 * A * (A * Bx + B * By + C)) / (A * A + B * B);
                double Byy = By - (2.0 * B * (A * Bx + B * By + C)) / (A * A + B * B);
                Bx = Bxx, By = Byy;
                Ax = hx, Ay = hy;
                ///看点B是否在出射线上
                if(sgn(Vx) != 0){
                    t1 = (Bx - Ax) / Vx;
                    if(sgn(By - Ay - Vy * t1) == 0){
                        flag = true;
                    }else{
                        flag = false;
                    }
                }
                else if(sgn(Vy) != 0){
                    t2 = (By - Ay) / Vy;
                    if(sgn(Bx - Ax - Vx * t2) == 0){
                        flag = true;
                    }else{
                        flag = false;
                    }
                }
            }
        }
        printf("%s\n", flag ? "Yes" : "No");
    }
    return 0;
}
/*
3
0 0 1
2 2 0 1
-1 -1
0 0 1
-1 2 1 -1
1 2
0 0 1
-2 3 1 -1
-1 2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值