HDU 2017 多校联合训练赛6 1002 6097 Mindis 反演变换 计算几何

来源:互联网 发布:java调用odata 编辑:程序博客网 时间:2024/06/05 06:22

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.
 

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
T500000
100x,y100
1r100
 

Output
For each case output one line denotes the answer.
The answer will be checked correct if its absolute or relative error doesn't exceed106.
Formally, let your answer be a, and the jury's answer be b. Your answer is considered correct if|ab|max(1,b)106.
 

Sample Input
444 00 440 33 040 22 040 11 0
 

Sample Output
5.65685435.65685435.89450306.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,圆内两点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;}