hdu 6097 Mindis(多校联赛)

来源:互联网 发布:依阿华级战列舰 知乎 编辑:程序博客网 时间:2024/06/04 19:33



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 exceed 106.
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 坐标(0, 0), 给定两点 P, Q(不在圆外),满足 PO = QO,

要在圆上找一点 D,使得 PD + QD 取到最小值。

这道题其实更接近一道数学题 知识要用代码去实现它 比赛做这道题的时候没有足够的精力取好好思考了 关键是没能想到做出辅助的反演点来解决  感觉反演点这个地方确实是巧妙 让我想到了以前的一道经典的数学题 :一条河的同一侧有两个村庄 现在要在河上建立一个供水站 向两个村庄供水 求在哪建供水站 向两个村庄的铺设的水管最短   当然答案大家都知道 就是把期中一个村庄以河为对称轴做对称点 然后把这个对称点与另一个村庄连接 连线与河流的交线就是正确的选址地点   

这里用到了反演的知识 其实反演点与对称点的作用类似 

过点P做P的反演点P1,使得OPOP1=OD2=r2 

OPOD=ODOP1=DPDP1
然后后面的计算与d点的求值 都会用到相关的比例 

当 P'Q' 与圆有交点时:

不妨设交点为 O',若 D 不为 O',则 P'D + Q'D >  P'Q'(三角形两边之和大于第三边);当且仅当 D 取 O' 时,P'Q + Q'D 取到最小值,即为 P'Q'。

当 P'Q' 与圆无交点时:

不妨将 P' 与 Q' 看成椭圆的两个焦点,当椭圆慢慢变大时,第一个碰到的圆上的点 D 即为使得 P'D + Q'D 最小的点;画个图就很显然了,第一个碰到的点即为 PQ 的中垂线与圆的交点。

至于判断有 P'Q' 与圆有没有交点,就是圆心到直线的距离与半径比较,又因为此处 P'O=Q'O,所以只需要比较 P'Q' 的中点到圆心的距离和半径的大小。

代码会给出更详细的解释:
ac代码:
#include <bits/stdc++.h>#define eps 1e-8 //确定一个最小值 用于后续的比较int main(){    int T;    scanf("%d", &T);    while (T--)    {        double r, x1, y1, x2, y2;        scanf("%lf%lf%lf%lf%lf", &r, &x1, &y1, &x2, &y2);//输入半径和点的坐标等信息        double d0 = sqrt(pow(x1, 2) + pow(y1, 2));        if (fabs(d0) < eps)//p和q两个点无限接近于原点        {            printf("%.7f\n", 2 * r);//此时的pd+qd就相当与两条半径            continue;        }        double k = r * r / (d0 * d0);//用这个比值 确定p和q的反演点        double x3 = x1 * k, x4 = x2 * k, y3 = y1 * k, y4 = y2 * k;//x3 y3 x4 y4分别是p和q对应的反演点        double mx = (x3+x4)/2, my = (y3+y4)/2, ans;//mx和my是 p和q反演点连线的中点坐标(以下用连线中点代替)        double d = sqrt(pow(mx,2)+pow(my,2));//原点(圆心)到pq反演点的中点的距离         if (d <= r)//判断反演点和半径的关系 如果两个反演点的中点到圆心的距离小于半径         {//即反演点之间的连线与圆相交或者相切 此时dp+qd的最小时的d点即是线与圆的交点            double dist = sqrt(pow(x3 - x4, 2) + pow(y3 - y4, 2));//长度符合反演点与原来点的比例关系            ans = dist * d0 / r;        }        else//其他的即是连线与圆相离时的状态 这时候的d点是p和q的反演点的连线的中垂线与圆的交点        {            double kk = r / d;//找出半径和连线中点到圆心距离的比例 即是现在的d点坐标与连线中点坐标的比例            double smx = mx * kk, smy = my * kk;//求出连线中点的坐标            ans = 2 *sqrt(pow(smx - x1, 2) + pow(smy - y1, 2));//求出最后连线的长度 乘2是因为此时pd=qd        }        printf("%.7f\n", ans);//注意保留小数的位数    }    return 0;}