MindisTime 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
Sample Output
Source
|
题意:
圆心 O 坐标(0, 0), 给定两点 P, Q(不在圆外),满足 PO = QO,
要在圆上找一点 D,使得 PD + QD 取到最小值。
这道题其实更接近一道数学题 知识要用代码去实现它 比赛做这道题的时候没有足够的精力取好好思考了 关键是没能想到做出辅助的反演点来解决 感觉反演点这个地方确实是巧妙 让我想到了以前的一道经典的数学题 :一条河的同一侧有两个村庄 现在要在河上建立一个供水站 向两个村庄供水 求在哪建供水站 向两个村庄的铺设的水管最短 当然答案大家都知道 就是把期中一个村庄以河为对称轴做对称点 然后把这个对称点与另一个村庄连接 连线与河流的交线就是正确的选址地点
这里用到了反演的知识 其实反演点与对称点的作用类似
过点P做P的反演点
P1
,使得
OP∗OP1=OD2=r2
当 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' 的中点到圆心的距离和半径的大小。
#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;
}