无聊的过河船同学和无聊的胀鱼同学非常喜欢打桌上冰球(其实只是喜欢听球碰撞时的声音)。在无聊的一天,无聊的过河船同学想到了一个无聊的玩法:两人同时将两个球放桌面上,同时击出,然后听两颗球撞在一起时的声音。然而他们都对击球的精确度把握得不是很好,所以这两颗球并不一定能相撞。
现在假设桌面无限大,并且绝对光滑,给出两球的初始位置、半径和运动速度,保证两球初始没有接触。无聊的过河船同学想知道两球能否相撞(接触即认为相撞),如果能,他想知道两球相撞的时间(从两人击球时开始计时),如果不能,他想知道全过程中两球距离的最小值,这里两球距离的定义为两球上任取两个点的距离的最小值,数据保证这种情况下答案不小于。请注意,冰球是个圆柱体,从空中往下看就是一个圆,且在这个问题中,冰球的高度可以忽略不计。
第一行是一个正整数,表示测试数据的组数,
每组测试数据包含两行,
第行包含五个绝对值不大于的整数,表示第个球的初始位置、半径和运动速度。
对于每组测试数据,若两球能相撞,输出两球相撞的时间,否则输出全过程中两球距离的最小值,相对误差不超过即可,
2 0 0 2 1 0 11 0 1 -1 0 0 0 2 1 0 11 5 1 -1 0
4.0000000000 2.0000000000
对于第一组样例,两球在击球后4.0秒时发生碰撞,
对于第二组样例,两球不发生碰撞,且在击球后5.5秒时两球距离最近,此时距离为2.0。
设时间为t时两个球相撞或者两个球距离最近,用t可以表示出两个球的坐标 Q1( x1+t*vx1 , y1+t*vy1) Q2( x2+t*vx2 , y2+t*vy2) ,
Q1Q2的距离= aqrt{(x1-x2)^2 +(y1-y2)^2 } =r1+r2; 整理方程得到一个关于t 的一元二次方程 sqrt( a t^2 + b t + c )= r1+r2;
求解该方程,分a=0和a!=0,a!=0,求解 △<0,△=0,△>0,当△<0时无解,求解方程左边的最小值 ,即 t = -b/(2*a),t>0, 带入方程得 d= sqrt ((4ac-b^2)/(4a) ),ans=d-(r1+r2); t<0 ,ans=sqrt(c)-(r1+r2) 。 当△>=0时,ans=min(t1,t2)。当a=0,时,ans=sqrt(c)-(r1+r2)。
又复习了一遍初中知识
#include <iostream> #include <stdio.h> #include <math.h> #include <string.h> using namespace std; double solve(double a,double b,double c) { double dat=b*b-4.0*a*c; if(dat<0) return -1; double x1= ( -b+sqrt(dat) )/2.0/a; double x2= ( -b-sqrt(dat) )/2.0/a; if(x2>=0.0) return x2; if(x2<0&&x1>0) return x1; if(x1<0&&x2<0) return -1; } int main() { int t; double x1,y1,r1,vx1,vy1; double x2,y2,r2,vx2,vy2; double x,vx,y,vy; double a,b,c; double R; scanf("%d",&t); while(t--) { scanf("%lf%lf%lf%lf%lf",&x1,&y1,&r1,&vx1,&vy1); scanf("%lf%lf%lf%lf%lf",&x2,&y2,&r2,&vx2,&vy2 ); x=x1-x2;///c y=y1-y2;///c vx=vx1-vx2;///a vy=vy1-vy2;///a a=vx*vx+vy*vy; c=x*x+y*y; b=2.0*(x*vx+y*vy); R=1.0*(r1+r2)*(r1+r2); double t=-1.0*b/2.0/a; if(a!=0) { double ans=solve(a,b,c-R); if(ans<0&&t<0) printf("%.10lf\n",sqrt(c)-r1-r2); if(ans<0&&t>=0) printf("%.10lf\n",sqrt( (4.0*a*c-b*b) /(4.0*a) )-r1-r2); if(ans>=0) printf("%.10lf\n",ans); } else printf("%.10lf\n",sqrt(c)-r1-r2); } return 0; }