BNU 51638 Air Hockey 三分二分法

Air Hockey

 

无聊的过河船同学和无聊的胀鱼同学非常喜欢打桌上冰球(其实只是喜欢听球碰撞时的声音)。在无聊的一天,无聊的过河船同学想到了一个无聊的玩法:两人同时将两个球放桌面上,同时击出,然后听两颗球撞在一起时的声音。然而他们都对击球的精确度把握得不是很好,所以这两颗球并不一定能相撞。

现在假设桌面无限大,并且绝对光滑,给出两球的初始位置、半径和运动速度,保证两球初始没有接触。无聊的过河船同学想知道两球能否相撞(接触即认为相撞),如果能,他想知道两球相撞的时间(从两人击球时开始计时),如果不能,他想知道全过程中两球距离的最小值,这里两球距离的定义为两球上任取两个点的距离的最小值,数据保证这种情况下答案不小于10^{-6}。请注意,冰球是个圆柱体,从空中往下看就是一个圆,且在这个问题中,冰球的高度可以忽略不计。

 

Input

第一行是一个正整数T(leq 10000),表示测试数据的组数,

每组测试数据包含两行,

i行包含五个绝对值不大于1000的整数x[i],y[i],r[i],vx[i],vy[i],表示第i个球的初始位置、半径和运动速度。

 

Output

对于每组测试数据,若两球能相撞,输出两球相撞的时间,否则输出全过程中两球距离的最小值,相对误差不超过10^{-6}即可,

也就是说,令输出结果为a,标准答案为b,若满足frac{ left | a-b 
ight | }{max(1,b)} leq 10^{-6},则输出结果会被认为是正确答案。
 

Sample Input

2
0 0 2 1 0
11 0 1 -1 0
0 0 2 1 0
11 5 1 -1 0

Sample Output

4.0000000000
2.0000000000

Hint

对于第一组样例,两球在击球后4.0秒时发生碰撞,

对于第二组样例,两球不发生碰撞,且在击球后5.5秒时两球距离最近,此时距离为2.0。

 

Source

Author

题解:

  

 两个不同大小的圆柱体向不同的方向以不同速度同时出发,问是否能碰撞,不能的话给出最小距离,能的话给出碰撞的时间。

解题思路:

      可以证明两球在运动过程中一定是先靠近再变远或者直接一直变远的,所以可以三分运动时间求两圆圆心的最近距离,如果两圆最近距离(圆心最近距离-两圆半径)小于1e-6(题干说的条件),则可以碰撞,再以0~达到最近点的时间为区间二分求出碰撞时间,否则直接输出两圆圆心最近距离减去两圆半径之和。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+20, M = 1e6+10, mod = 1e9+7, inf = 1e9+1000;
typedef long long ll;
double x[2], y[2], r[2], vx[2], vy[2];
inline double s(double a)
{
    return a*a;
}
double d(double t)
{
    double x0 = x[0] + vx[0] * t;
    double x1 = x[1] + vx[1] * t;
    double y0 = y[0] + vy[0] * t;
    double y1 = y[1] + vy[1] * t;
    return sqrt(s(x1 - x0) + s(y1 - y0)) - r[0] - r[1];
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%lf%lf%lf%lf%lf", &x[0], &y[0], &r[0], &vx[0], &vy[0]);
        scanf("%lf%lf%lf%lf%lf", &x[1], &y[1], &r[1], &vx[1], &vy[1]);
        double l = 0, r = 1e10;
        while (r - l>1e-7)//先三分找最低点
        {
            double mid1 = l + (r - l) / 3, mid2 = r - (r - l) / 3;
            double ans1 = d(mid1), ans2 = d(mid2);
            if (ans1 <= ans2) r = mid2;
            else l = mid1;
        }
        if (d(l)>=1e-6)printf("%.10lf
", d(l));
        else//二分找0点
        {
            l = 0;
            while (r - l > 1e-7)
            {
                double mid = (r + l) / 2;
                if (d(mid) > 0)l = mid;
                else r = mid;
            }
            printf("%.10f
", l);
        }
    }
}
原文地址:https://www.cnblogs.com/zxhl/p/5453077.html