传送门:https://www.bnuoj.com/v3/contest_show.php?cid=7785#problem/D
Description
无聊的过河船同学和无聊的胀鱼同学非常喜欢打桌上冰球(其实只是喜欢听球碰撞时的声音)。在无聊的一天,无聊的过河船同学想到了一个无聊的玩法:两人同时将两个球放桌面上,同时击出,然后听两颗球撞在一起时的声音。然而他们都对击球的精确度把握得不是很好,所以这两颗球并不一定能相撞。
现在假设桌面无限大,并且绝对光滑,给出两球的初始位置、半径和运动速度,保证两球初始没有接触。无聊的过河船同学想知道两球能否相撞(接触即认为相撞),如果能,他想知道两球相撞的时间(从两人击球时开始计时),如果不能,他想知道全过程中两球距离的最小值,这里两球距离的定义为两球上任取两个点的距离的最小值,数据保证这种情况下答案不小于 10 −6 。请注意,冰球是个圆柱体,从空中往下看就是一个圆,且在这个问题中,冰球的高度可以忽略不计。
Input
第一行是一个正整数 T(≤10000) ,表示测试数据的组数,
每组测试数据包含两行,
第
i
行包含五个绝对值不大于
对于每组测试数据,若两球能相撞,输出两球相撞的时间,否则输出全过程中两球距离的最小值,相对误差不超过 10 −6 即可,
也就是说,令输出结果为
a
,标准答案为
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;
}