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

来源:互联网 发布:飞鱼打印软件 编辑:程序博客网 时间:2024/05/17 04:43

传送门

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 的单位向量相等就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;}
阅读全文
0 0
原创粉丝点击