您的位置:首页 > 其它

hdu 6097 Mindis(多校联赛)

2017-08-11 20:39 459 查看

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
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.

 

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

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

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

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

过点P做P的反演点P1,使得OP∗OP1=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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: