HDU 5572 An Easy Physics Problem(计算几何)——2015ACM/ICPC亚洲区上海站-重现赛

传送门

An Easy Physics Problem

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2322    Accepted Submission(s): 469


Problem Description
On an infinite smooth table, there’s a big round fixed cylinder and a little ball whose volume can be ignored.

Currently the ball stands still at point A , then we’ll give it an initial speed and a direction. If the ball hits the cylinder, it will bounce back with no energy losses.

We’re just curious about whether the ball will pass point B after some time.
 

Input
First line contains an integer T , which indicates the number of test cases.

Every test case contains three lines.

The first line contains three integers Ox, Oy and r , indicating the center of cylinder is (Ox,Oy) and its radius is r .

The second line contains four integers Ax, Ay , Vx and Vy , indicating the coordinate of A is (Ax,Ay) and the initial direction vector is (Vx,Vy) .

The last line contains two integers Bx and By , indicating the coordinate of point B is (Bx,By).

1 ≤ T ≤ 100.

| Ox |,| Oy |≤ 1000.

1 ≤ r ≤ 100.

| Ax |,| Ay |,| Bx |,| By |≤ 1000.

| Vx |,| Vy |≤ 1000.

Vx0 or Vy0 .

both A and B are outside of the cylinder and they are not at same position.
 

Output
For every test case, you should output " Case #x: y", where x indicates the case number and counts from 1. y is "Yes" if the ball will pass point B after some time, otherwise y is "No".
 

Sample Input
2
0 0 1
2 2 0 1
-1 -1
0 0 1
-1 2 1 -1
1 2
 

Sample Output
Case #1: No
Case #2: Yes

题目大意:

现在有一个实心的圆,圆心坐标 (Ox,Oy) 和半径 r 已知,现在给定一个小球,可以视为质点 A(Ax,Ay) 和这个小球的速度方向 V(Vx,Vy) ,如果球碰到实心圆,它会反弹而不会产生能量损失。 还给定了一个点 B(Bx,By) ,现在问:小球经过一段时间后,是否能够到达 B 点。

解题思路:

首先我们判断一下小球的轨迹与圆的位置关系,如果小球的轨迹与圆相交,那么就会发生反射,否则小球就会沿直线一直走下去。现在就有两种情况:
1) 相交
2) 不相交
如果是第一种情况的话,我们求出小球的轨迹与圆的相交的第一个点的坐标 T,然后找 A 点关于 T 点和 O 点(圆心坐标)所组成的直线的对称点 A ,现在需要观察 A 点是否在 TB 所在的线段上,如果在就能到达 B 点,否则不能到达,在这种情况下,还有一种特殊的情况就是不需要反射就能到达 B 点的,需要特殊判断一下,即判断 B 点是不是在 AT 所在的线段上

如果是第二种情况的话就简单了,我们只需要判断向量 AB 的单位向量是否与向量 V <script type="math/tex" id="MathJax-Element-18954">V</script> 的单位向量相等就OK了。

代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
using namespace std;
const double eps = 1e-8;
// 几何误差修正
inline int cmp(double x) {
    return x < -eps ? -1 : (x > eps);
}

// 计算x的平方
inline double sqr(double x) {
    return x * x;
}

// 开方误差修正
inline double mySqrt(double n) {
    return sqrt(max((double)0, n));
}

// 二维点(向量)类
struct Point {
    double x, y;
    Point() {}
    Point(double x, double y): x(x), y(y) {}
    void input() {
        scanf("%lf%lf", &x, &y);
    }
    friend Point operator + (const Point& a, const Point& b) {
        return Point(a.x + b.x, a.y + b.y);
    }
    friend Point operator - (const Point& a, const Point& b) {
        return Point(a.x - b.x, a.y - b.y);
    }
    friend bool operator == (const Point& a, const Point& b) {
        return cmp(a.x - b.x) == 0 && cmp(a.y - b.y) == 0;
    }
    friend Point operator * (const Point& a, const double& b) {
        return Point(a.x * b, a.y * b);
    }
    friend Point operator * (const double& a, const Point& b) {
        return Point(a * b.x, a * b.y);
    }
    friend Point operator / (const Point& a, const double& b) {
        return Point(a.x / b, a.y / b);
    }
    // 返回本向量的长度
    double norm() {
        return sqrt(sqr(x) + sqr(y));
    }
    // 返回本向量对应的单位向量
    Point unit() {
        return Point(x, y) / norm();
    }
};

// 求向量间叉积
double det(const Point& a, const Point& b) {
    return a.x * b.y - a.y * b.x;
}

// 求向量间点积
double dot(const Point &a, const Point& b) {
    return a.x * b.x + a.y * b.y;
}

// 求点到直线的投影
Point getLineProjection(Point P, Point A, Point B) {//直线AB
    Point v = B - A;
    return A + v * dot(v, P - A) / dot(v, v);
}

// 求点关于直线的对称点
Point mirrorPoint(Point p, Point s, Point t) {//直线 st
    Point i = getLineProjection(p, s, t);
    return 2 * i - p;
}

// 判断点是否在直线上
bool pointOnSegment(Point p, Point s, Point t) {
    return cmp(det(p - s, t - s)) == 0 && cmp(dot(p - s, p - t)) <= 0;
}

// 求直线(线段、射线)与圆相交的交点
//ret[0]:第一个交点 ret[1]:第二个交点
void circleCrossLine(Point a, Point b, Point o, double r, Point ret[], int& num) {
    double x0 = o.x, y0 = o.y;
    double x1 = a.x, y1 = a.y;
    double x2 = b.x, y2 = b.y;
    double dx = x2 - x1, dy = y2 - y1;
    double A = sqr(dx) + sqr(dy);
    double B = 2 * dx * (x1 - x0) + 2 * dy * (y1 - y0);
    double C = sqr(x1 - x0) + sqr(y1 - y0) - sqr(r);
    double delta = sqr(B) - 4 * A * C;
    num = 0;
    if(cmp(delta) < 0)  return;
    double t1 = (-B - mySqrt(delta)) / (2 * A);
    double t2 = (-B + mySqrt(delta)) / (2 * A);
    // 交点只能在射线上
    if(cmp(t1) >= 0)
        ret[num++] = Point(x1 + t1 * dx, y1 + t1 * dy);
    // 交点只能在射线上
    if(cmp(t2) >= 0)
        ret[num++] = Point(x1 + t2 * dx, y1 + t2 * dy);
}
Point A, V, B, O;
double r;
Point ret[5];
int Judge(){
    int num;
    circleCrossLine(A, A+V, O, r, ret, num);
    if(num < 2){
        Point t = B - A;
        if(t.unit() == V.unit()) return 1;
        return 0;
    }
    else{
        Point t = mirrorPoint(A, O, ret[0]);
        if(pointOnSegment(B, A, ret[0])) return 1;
        if(pointOnSegment(t, ret[0], B)) return 1;
        return 0;
    }
}
int main(){
    int T;
    scanf("%d", &T);
    for(int cas=1; cas<=T; cas++){
        scanf("%lf%lf%lf", &O.x, &O.y, &r);
        A.input();
        V.input();
        B.input();
        printf("Case #%d: ",cas);
        if(Judge()) puts("Yes");
        else puts("No");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值