Aizu 2537 Billiard(台球碰撞模拟)

题意:给N (2N11) 个圆,半径都为r,圆心位置不同,都在一个矩形内。矩形左下角是(0,0),右上角是(W,H)。保证初始每个圆都不相交或相切。要求打第一个圆,初始方向是(Vx, Vy),这个圆会不断地与矩形边界发生无能量损失碰撞,镜面反射。小圆只能运动10000单位长度,问小圆碰撞到的第一个圆是哪个,或者不会发生碰撞就停下了。

链接:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2537

解法:就模拟,但是不同的优化会使得代码量级别不同。做一个优化,将第一个圆看成一个点,就是它的圆心,其他圆半径扩大一倍,将矩形缩小,上左下右边界分别向下、向右、向上、向左移动 r。然后就变成一条射线发生镜面反射,问第一个碰到的圆是哪一个即可。。直接模拟。

//Hello. I'm Peter.
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
using namespace std;
#define peter cout<<"i am peter"<<endl
typedef long long ll;

const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
    if(fabs(x) < eps) return 0;
    else return x > 0 ? 1 : -1;
}
struct Point{
    double x, y;
    Point(){};
    Point(double x1, double y1){
        x = x1, y = y1;
    }
};
typedef Point Vector;
Vector operator - (Vector a, Vector b){
    return Vector(a.x - b.x, a.y - b.y);
}
Vector operator + (Vector a, Vector b){
    return Vector(a.x + b.x, a.y + b.y);
}
double operator % (Vector a, Vector b){
    return a.x * b.y - a.y * b.x;
}
double operator * (Vector a, Vector b){
    return a.x * b.x + a.y * b.y;
}
Vector operator * (Vector a, double b){
    return Vector(a.x * b, a.y * b);
}
Point getp(Point p1, Vector v1, Point p2, Vector v2, double &t){
    t = ((p2 - p1) % v2) / (v1 % v2);
    return p1 + v1 * t;
}
double Length(Vector v){
    return sqrt(v.x * v.x + v.y * v.y);
}
double angle(Vector v){
    return atan2(v.y, v.x);
}
double angle(Vector v1, Vector v2){
    double a = angle(v1) - angle(v2);
    while(sgn(a) < 0) a += 2 * pi;
    while(sgn(a - 2*pi) >= 0) a -= 2 * pi;
    a = fmin(a, 2 * pi - a);
    return a;
}
Vector Rotate(Vector v, double rad){
    return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));
}
Vector reflect(Vector v, Vector minor){
    if(sgn(v * minor) < 0) minor = minor * (-1);
    double a = angle(v, minor);
    if(sgn(v % minor) > 0) v = Rotate(v, 2 * a);
    else v = Rotate(v, - 2 * a);
    return v;
}
double dis(Point p, Point p1, Vector v1){
    return fabs((p - p1) % v1) / Length(v1);
}

int n;
struct Wall{
    Point p[2];
    Vector v;
}wall[10];
struct Circle{
    Point p;
    double r;
}cir[30];

double get(Point p, Vector v, Circle cir){
    double a = v.x, b = p.x - cir.p.x, c = v.y, d = p.y - cir.p.y;
    double e = a * a + c * c, f = 2 * (a * b + c * d), g = b * b + d * d - cir.r * cir.r;
    double delta = f * f - 4 * e * g;
    double t1, t2;
    t1 = (-f - sqrt(delta)) / (2 * e);
    t2 = (-f + sqrt(delta)) / (2 * e);
    Point p1, p2;
    p1 = p + v * t1, p2 = p + v * t2;
    return fmin(Length(p1 - p), Length(p2 - p));
}
const double maxid = 10000;
int solve(Point nowp, Vector nowv, double nowdis){
    double mini = 1e9;
    int w = 0;
    for(int i = 2; i <= n; i++){
        double d = dis(cir[i].p, nowp, nowv);
        if(sgn(d - cir[i].r) <= 0){
            if(sgn((cir[i].p - nowp) * nowv) > 0){
                double dis = get(nowp, nowv, cir[i]);
                if(sgn(dis - mini) < 0){
                    mini = dis;
                    w = i;
                }
            }
        }
    }
    if(w != 0){
        double dis = get(nowp, nowv, cir[w]);
        if(sgn(nowdis + dis - maxid) > 0) return -1;
        else return w;
    }

    int nmaxi = 0;
    w = 0;
    double maxdis = -1;
    Point px = Point(0, 0);
    for(int i = 1; i <= 4; i++){
        if(sgn(wall[i].v % nowv) == 0) continue;
        double t;
        Point x = getp(wall[i].p[0], wall[i].v, nowp, nowv, t);
        if(sgn(t) >= 0 && sgn(t - 1) <= 0 && sgn((x - nowp) * nowv) > 0){
            double dis = Length(x - nowp);
            if(sgn(dis - maxdis) > 0){
                maxdis = dis;
                nmaxi = 1;
                px = x;
                w = i;
            }
            else if(sgn(dis - maxdis) == 0){
                nmaxi++;
            }
        }
    }
    double dis = Length(px - nowp);
    if(sgn(nowdis + dis - maxid) >= 0) return -1;
    nowdis += dis;
    nowp = px;
    if(nmaxi == 2) nowv = nowv * (-1);
    else nowv = reflect(nowv, wall[w].v);

    return solve(nowp, nowv, nowdis);
}

int main(){
    int w, h, r, x, y;
    while(scanf("%d",&n) == 1 && n != 0){
        scanf("%d%d%d%d%d",&w,&h,&r,&x,&y);
        Point p[10];
        p[0] = Point(0, 0) + Vector(r, r);
        p[1] = Point(w, 0) + Vector(-r, r);
        p[2] = Point(w, h) + Vector(-r, -r);
        p[3] = Point(0, h) + Vector(r, -r);
        int t = 0;
        for(int i = 1; i <= 4; i++){
            wall[i].p[0] = p[t];
            wall[i].p[1] = p[(t + 1) % 4];
            wall[i].v = wall[i].p[1] - wall[i].p[0];
            t++;
        }

        for(int i = 1; i <= n; i++){
            int x, y;
            scanf("%d%d",&x,&y);
            cir[i].p.x = x, cir[i].p.y = y;
            cir[i].r = 2 * r;
        }

        printf("%d\n",solve(cir[1].p, Vector(x, y), 0));
    }
    return 0;
}

你可能感兴趣的:(Aizu 2537 Billiard(台球碰撞模拟))