Description
给出一个圆心为 O(0,0) ,半径为 r 的圆,并给出圆内两个距圆心等距的点 P,Q ,要求在圆上找一个点 D ,使得 |PD|+|QD| 最小,输出最小值
Input
第一行一个整数 T 表示用例组数,每组用例首先输入一整数 r 表示圆的半径,之后输入 P,Q 两点的横纵坐标 xp,yp,xq,yq ,保证 |OP|=|OQ| (T≤500000,−100≤xp,yp,xq,yq≤100,1≤r≤100)
Output
对于每组用例,输出 |PD|+|QD| 的最小值,结果和精确值的相对误差及绝对误差不超过 10−6
Sample Input
4
4
4 0
0 4
4
0 3
3 0
4
0 2
2 0
4
0 1
1 0
Sample Output
5.6568543
5.6568543
5.8945030
6.7359174
Solution
考虑 P 点关于圆心的反演点 P′ ,由 |OP||OP′|=r2 知 r|OP′|=|OP|r ,即 △OPD ~ △ODP′ ,同理对于 Q 点关于圆心的反演点 Q′ 有 △OQD ~ △ODQ′ ,相似比为 |OP|r ,且有 △OPQ ~ △OP′Q′ ,相似比为 |PQ||P′Q′|=|OP|2r2
如果 |OP|=0 ,说明 P,Q,O 重合,显然答案是 2r
如果 |OP|>0 ,由于 |PD|+|QD|=r|OP|(|P′D|+|Q′D|) ,若 P′Q′ 与圆相交则显然右端最小值为 r|OP||P′Q′|=|OP|r|PQ| ,如果不相交则 D 点取线段 PQ 的中垂线和圆的交点时 |P′D|+|Q′D| 最小,此时通过求出线段 PQ 中点 R 的坐标及 |OD||OR|=r|OR 可以得到 D 点坐标
Code
#include
#include
using namespace std;
#define eps 1e-8
int sign(double x)
{
if(fabs(x)return 0;
if(x>eps)return 1;
return -1;
}
double dis(double x0,double y0,double x1,double y1)
{
double x=x1-x0,y=y1-y0;
return sqrt(x*x+y*y);
}
int main()
{
int T;
double xp,yp,xq,yq,r;
scanf("%d",&T);
while(T--)
{
scanf("%lf%lf%lf%lf%lf",&r,&xp,&yp,&xq,&yq);
double OP=dis(xp,yp,0,0),ans;
if(sign(OP)==0)ans=2.0*r;
else
{
double xr=0.5*(xp+xq),yr=0.5*(yp+yq);
double OR=dis(xr,yr,0,0);
double ORR=OR*r*r/(OP*OP);
if(sign(ORR-r)<=0)ans=dis(xp,yp,xq,yq)*r/OP;
else
{
double xs=xr*r/OR,ys=yr*r/OR;
ans=2.0*dis(xp,yp,xs,ys);
}
}
printf("%.10f\n",ans+eps);
}
return 0;
}