【BNUOJ】【第十四届北京师范大学程序设计竞赛决赛】D. Air Hockey

传送门:https://www.bnuoj.com/v3/contest_show.php?cid=7785#problem/D

Description 

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

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

Input 

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

每组测试数据包含两行,

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

Output 

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

也就是说,令输出结果为 a  ,标准答案为b ,若满足 |ab|max(1,b) 10 6   ,则输出结果会被认为是正确答案。

SampleInput 

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

SampleOutput 

4.0000000000
2.0000000000

Hint 

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

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

Solution 
嗯哼……一眼看出了两个球之间的距离是单峰的……
枚举时间 t  <script id="MathJax-Element-3113" type="math/tex">t</script>时,可以直接算出两个球的位置(用初速度和初位置),然后两点间距离公式就可以表示距离。
既然距离是单峰的,那就三分……
三分可以得到它们之间距离最小的时间。但是,它们可能已经相撞了。所以在这种情况下,我们还需要去计算相撞时间。
注意到一个性质,我们刚刚取到了一个单峰函数的最值……把这个单峰函数从这切开就是单调的了……
既然距离是单调的,那就二分……直接二分相撞时间点。

唯一坑:注意精度……

#include <iostream>
#include <cstdio>
#include <cmath>
#define y2 ijh
#define y1 dfg
using namespace std;
int t,x1,y1,r1,x2,y2,r2,vx1,vx2,vy1,vy2,ro;
double l,r,mid,lm,rm;

double cal(double tim)
{
    double x=x1+vx1*tim-x2-vx2*tim;
    double y=y1+vy1*tim-y2-vy2*tim;
    return (x*x+y*y);
}

int main()
{
    scanf("%d",&t);
    for (int i=1;i<=t;i++)
    {
        scanf("%d%d%d%d%d",&x1,&y1,&r1,&vx1,&vy1);
        scanf("%d%d%d%d%d",&x2,&y2,&r2,&vx2,&vy2);
        ro=(r1+r2)*(r1+r2);
        l=(double)0;
        r=(double)20000;
        while (l+0.0000000001<r)
        {
            lm=(r-l)/3;
            rm=l+lm*2;
            lm=l+lm;
            if (cal(lm)<cal(rm)) r=rm; else l=lm;
        }
        if (cal(l)<=ro)
        {
            r=l;
            l=(double)0;
            while (l+0.0000000001<r) 
            {
                mid=(l+r)/2;
                if (cal(mid)>ro) l=mid; else r=mid;
            }
            printf("%.12lf\n",l);
        }
        else printf("%.12lf\n",sqrt(cal(l))-sqrt(ro));
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值