Gym 100825 H - Trick Shot[计算几何]

题意:给了一场长 w ,宽l的台球桌,以桌子左下为原点,给了 1,2,3 三个球的坐标,给定了球的半径 r 和白球的纵坐标, 问是否可能白球撞到1号球, 1 号球撞向3号球使其进入右上角。白球撞到 1 号球后撞向2号球使其进入左上角,如果可能,输出白球的起始横坐标和发射方向与x轴正轴的夹角度数。

分析:我们反过来推,通过右上角的点 P(w,l) 3 号球的坐标,我们可以得到1号球 和 3 号球 相撞时的坐标P2(x2,y2), 对于 2 号球和(0,l)也同理。通过 P2 1 号球的坐标能得到白球和1号球相撞时的坐标 P3 。这样就能求出最先白球的位置。
注意点是: 入射角是不可能大于 90 °的,还有就是白球在到达 P3 之前可能撞到 2,3 号球。白球的横坐标范围是 [r,wr]

以下是代码:

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
#define lson l,mid,id<<1
#define rson mid+1,r,id<<1|1
#define zero(x) (((x)>0?(x):-(x))<eps)
typedef pair<int, int>pii;
typedef pair<ll, ll>pll;
typedef pair<double, double>pdd;

const double eps = 1e-8;
const int maxn = 300010;
const int MAXM = 100005;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const double FINF = 1000000000000000.0;
const ll MOD = 100000007;
const double PI = acos(-1);

struct Point {
    long double x, y;
    Point(long double _x = 0, long double _y = 0) :x(_x), y(_y) {}
    void print() {
        cout << x << " " << y << endl;
    }
    friend long double dot(Point a, Point b) {
        return a.x * b.x + a.y * b.y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x - b.x, y - b.y);
    }
    double operator *(const Point &b)const
    {
        return x*b.x + y*b.y;
    }
};
double dist(Point a, Point b)
{
    return sqrt((a - b)*(a - b));
}
struct line {
    long double A, B, C;
    line(long double _A = 0, long double _B = 0, long double _C = 0) :A(_A), B(_B), C(_C) {}
};
struct Line
{
    Point s, e;
    Line() {}
    Line(Point _s, Point _e)
    {
        s = _s; e = _e;
    }
};
long double r, h;
Point getpoint(Point a, Point b) {
    if (fabs(a.x - b.x) < eps)return Point(a.x, a.y - 5);
    long double k = (b.y - a.y) / (b.x - a.x), bb = a.y - k * a.x;
    long double A = k * k + 1, B = 2 * bb * k - 2 * a.y * k - 2 * a.x, C = a.x * a.x - 4 * r * r + (bb - a.y) * (bb - a.y);
    long double dt = B * B - 4 * A * C;
    long double x1 = (-B - sqrt(dt)) / (2 * A), x2 = (-B + sqrt(dt)) / (2 * A);
    if (a.x > b.x)return Point(x2, k * x2 + bb);
    else return Point(x1, k * x1 + bb);
}
line getline(Point a, Point b) {
    long double A = b.y - a.y, B = a.x - b.x, C = a.y * (b.x - a.x) - (b.y - a.y) * a.x;
    return line(A, B, C);
}
Point NearestPointToLineSeg(Point P, Line L)
{
    Point result;
    double t = ((P - L.s)*(L.e - L.s)) / ((L.e - L.s)*(L.e - L.s));
    if (t >= 0 && t <= 1)
    {
        result.x = L.s.x + (L.e.x - L.s.x)*t;
        result.y = L.s.y + (L.e.y - L.s.y)*t;
    }
    else
    {
        if (dist(P, L.s) < dist(P, L.e))
            result = L.s;
        else result = L.e;
    }
    return result;
}
Point p[4];
int main() {
    long double w, l;
    cin >> w >> l;
    cin >> r >> p[1].x >> p[1].y >> p[2].x >> p[2].y >> p[3].x >> p[3].y >> h;
    Point a1 = getpoint(p[3], Point(w, l));
    Point a2 = getpoint(p[2], Point(0, l));
    Point a3 = getpoint(p[1], a1);
    Point t1 = Point(a2.x - a3.x, a2.y - a3.y), t2 = Point(a3.x - p[1].x, a3.y - p[1].y);
    if (dot(t1, t2) < 0) {
        printf("impossible\n");
        return 0;
    }
    t1 = Point(a1.x - p[1].x, a1.y - p[1].y), t2 = Point(w - p[3].x, l - p[3].y);
    if (dot(t1, t2) < 0) {
        printf("impossible\n");
        return 0;
    }
    t1 = Point(a2.x - a3.x, a2.y - a3.y), t2 = Point(0 - p[2].x, l - p[2].y);
    if (dot(t1, t2) < 0) {
        printf("impossible\n");
        return 0;
    }
    line L1 = getline(a3, a2), L2 = getline(a1, p[1]);
    long double A1 = L2.A, B1 = L2.B, C1 = L2.C;
    long double A2 = L1.A, B2 = L1.B, C2 = L1.C;
    long double A3 = (A2 * (A1 * A1 + B1 * B1) - A1 * (2 * A1 * A2 + 2 * B1 * B2)),
        B3 = (B2 * (A1 * A1 + B1 * B1) - B1 * (2 * A1 * A2 + 2 * B1 * B2)),
        C3 = (C2 * (A1 * A1 + B1 * B1) - C1 * (2 * A1 * A2 + 2 * B1 * B2));
    long double ansd = -(B3 * h + C3) / A3, ansth = atan2((a3.y - h), (a3.x - ansd));
    Point ans = Point(ansd, h);
    Line l1 = Line(ans, a3);
    Point aaa = NearestPointToLineSeg(p[2], l1);
    Point bbb = NearestPointToLineSeg(p[3], l1);
    if (dist(p[2], aaa) + eps < 2 * r || dist(p[3], bbb) + eps < 2 * r) {
        printf("impossible\n");
        return 0;
    }
    ansth = ansth * 180 / acos(-1);
    if (abs(B3) < eps)ansth = 90;
    if (ansd < r || ansd > w - r)printf("impossible\n");
    else printf("%.2f %.2f\n", (double)ansd, (double)ansth);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值