Mindis
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Special Judge
Problem Description
The center coordinate of the circle C is O, the coordinate of O is (0,0) , and the radius is r.
P and Q are two points not outside the circle, and PO = QO.
You need to find a point D on the circle, which makes PD+QD minimum.
Output minimum distance sum.
P and Q are two points not outside the circle, and PO = QO.
You need to find a point D on the circle, which makes PD+QD minimum.
Output minimum distance sum.
Input
The first line of the input gives the number of test cases T; T test cases follow.
Each case begins with one line with r : the radius of the circle C.
Next two line each line contains two integers x , y denotes the coordinate of P and Q.
Limits
T≤500000
−100≤x,y≤100
1≤r≤100
Each case begins with one line with r : the radius of the circle C.
Next two line each line contains two integers x , y denotes the coordinate of P and Q.
Limits
T≤500000
−100≤x,y≤100
1≤r≤100
Output
For each case output one line denotes the answer.
The answer will be checked correct if its absolute or relative error doesn't exceed 10−6 .
Formally, let your answer be a, and the jury's answer be b. Your answer is considered correct if |a−b|max(1,b)≤10−6 .
The answer will be checked correct if its absolute or relative error doesn't exceed 10−6 .
Formally, let your answer be a, and the jury's answer be b. Your answer is considered correct if |a−b|max(1,b)≤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
Source
2017 Multi-University Training Contest - Team 6
题目大意
已知一个圆心O在原点上的圆,半径为r,圆内有两点P,Q,满足OP = OQ,要求找到在圆上一点D,使PD+QD最小,输出这个最小的距离和。
题目分析
题目要求圆上一点到圆内两点的距离和最小,我们首先能够想到的就是椭圆具有这样的性质。但是,如果我们以给出的两点P,Q为焦点做椭圆,首先,我们并不能直观地分析椭圆与圆的内切情况,切点有一个还是两个,其次,我们也不容易直接计算椭圆的短半轴和长半轴,只能通过二分逼近短半轴b,找到椭圆与圆相切的情况,从而运用椭圆的特性a^2 = b^2+c^2,得到长轴2a。(具体过程请参考博客http://www.cnblogs.com/chen9510/p/7341215.html)
官方题解给出了反演变换的思路,求得P,Q两点关于圆O的反演点A,B,利用以A,B为焦点的椭圆直接分析计算出最小的距离和。
首先,反演点的定义,已知圆O的半径为R,从圆心O出发任作一射线,在射线上任取两点M,N。若OM=m,ON=n,且mn=R^2,则称点M,N是关于圆O的反演点.M,N关于圆O的一个变换叫做反演变换。(摘自百度百科)
如图,P,Q为圆内两点,D为圆上一点,A,B分别为P,Q关于圆O的反演点,M为AB中点。
由反演点的定义得,OP*OA=r*r,等式变换得OP/r=r/OA,我们发现,等式刚好是三角形OAD~三角形ODP的相似比表达式,设两三角形的相似比为rate=OP/r,那么PD/AD = rate。因为OP=OQ,所以OQ的情况与OP是完全一样的。现在我们就将求PD+QD的最小值转化为求AD+BD的最小值,结果在乘以相似比即可。
在这里我们要注意到,反演点A,B的连线AB与圆O的位置关系有两种情况,第一种,AB与圆O有交点(相交或相切),那么AD+BD的最小值就是线段AB的长(两点之间线段最短);第二种,AB与圆O相离,那么我们需要以AB为焦点做椭圆,找到与圆相切的椭圆,切点即我们要找的D点,即AD+BD最小。并且,因为OP,OQ两边是完全对称的,所以D点一定在PQ的垂直平分线上。然后根据两点之间距离公式和勾股定理就能直接计算出AD+BD的值。
此外还有一种极限情况,如果P,Q两点重合,那么我们要找的D点就是OP(Q)所在半径与圆的交点。
代码
题目大意
已知一个圆心O在原点上的圆,半径为r,圆内有两点P,Q,满足OP = OQ,要求找到在圆上一点D,使PD+QD最小,输出这个最小的距离和。
题目分析
题目要求圆上一点到圆内两点的距离和最小,我们首先能够想到的就是椭圆具有这样的性质。但是,如果我们以给出的两点P,Q为焦点做椭圆,首先,我们并不能直观地分析椭圆与圆的内切情况,切点有一个还是两个,其次,我们也不容易直接计算椭圆的短半轴和长半轴,只能通过二分逼近短半轴b,找到椭圆与圆相切的情况,从而运用椭圆的特性a^2 = b^2+c^2,得到长轴2a。(具体过程请参考博客http://www.cnblogs.com/chen9510/p/7341215.html)
官方题解给出了反演变换的思路,求得P,Q两点关于圆O的反演点A,B,利用以A,B为焦点的椭圆直接分析计算出最小的距离和。
首先,反演点的定义,已知圆O的半径为R,从圆心O出发任作一射线,在射线上任取两点M,N。若OM=m,ON=n,且mn=R^2,则称点M,N是关于圆O的反演点.M,N关于圆O的一个变换叫做反演变换。(摘自百度百科)
由反演点的定义得,OP*OA=r*r,等式变换得OP/r=r/OA,我们发现,等式刚好是三角形OAD~三角形ODP的相似比表达式,设两三角形的相似比为rate=OP/r,那么PD/AD = rate。因为OP=OQ,所以OQ的情况与OP是完全一样的。现在我们就将求PD+QD的最小值转化为求AD+BD的最小值,结果在乘以相似比即可。
在这里我们要注意到,反演点A,B的连线AB与圆O的位置关系有两种情况,第一种,AB与圆O有交点(相交或相切),那么AD+BD的最小值就是线段AB的长(两点之间线段最短);第二种,AB与圆O相离,那么我们需要以AB为焦点做椭圆,找到与圆相切的椭圆,切点即我们要找的D点,即AD+BD最小。并且,因为OP,OQ两边是完全对称的,所以D点一定在PQ的垂直平分线上。然后根据两点之间距离公式和勾股定理就能直接计算出AD+BD的值。
此外还有一种极限情况,如果P,Q两点重合,那么我们要找的D点就是OP(Q)所在半径与圆的交点。
代码
//已知圆心O,圆内两点P,Q,满足OP = OQ,求P,Q反演点A,B,AB中点为M,找到在圆上一点D,使PD+QD最小
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const double acc = 1e-8;
int T;
double r, px, py, qx, qy;
double ax, ay, bx, by;
double dop, doa, dab, dam, dom, dmd, dad;//两点之间的距离d(op)
double rate, ans;//rate:相似比
double dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
scanf ("%d",&T);
while (T--)
{
scanf ("%lf",&r);
scanf ("%lf %lf",&px,&py);
scanf ("%lf %lf",&qx,&qy);
dop = dis(0, 0, px, py);
if (fabs(px-qx)<=acc && fabs(py-qy)<=acc)//如果两个点重合
{
printf("%.7f\n",(r-dop)*2);
}
else
{
rate = dop/r;
doa = r*r/dop;//oa*op = r*r
double rate2 = dop/doa;
ax = px/rate2;
ay = py/rate2;
bx = qx/rate2;
by = qy/rate2;
dab = dis(ax, ay, bx, by);
dam = dab/2.0;
dom = sqrt(doa*doa-dam*dam);
if (dom > r)
{
dmd = dom-r;
dad = sqrt(dmd*dmd+dam*dam);
ans = 2*dad*rate;
}
else
{
ans = dab*rate;
}
printf("%.7f\n",ans);
}
}
return 0;
}