题意:有一个圆心O在原点半径为r的圆,圆上或圆内有两个到圆心距离相等的点P和Q,要求在圆上找到一个点D,使得PD+QD最小。
官方题解:
分析:看了官方题解之后,自己画了图,首先分别作出P和Q的反演点P'和Q'(反演点的一个性质,OP×OQ=r×r),然后判断P'Q'与圆的关系,如果P'Q'与圆相交的话,那么min(PD+QD)就为P'Q'×d/r(其中d为OP的长度,这个是根据相似三角形得到的,一定要画图哇);反之,如果P'Q'与圆相离的话,那么D点就为P'Q'的中垂线与圆心的交点,求出这个交点,就能得到答案(交点的求法:根据比例,PQ中点坐标/中点到圆心距离=交点坐标/半径r)。
参考代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define eps 1e-8
double r;
double px,py;//P点
double ppx,ppy;//P的反演点
double pd;//长度
double qx,qy;//Q点
double qqx,qqy;//Q的反演点
double fx,fy;//反演点的中点
double fd;
double ans;
double ratio;
double dist( double x1, double y1, double x2, double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
int T;
scanf("%d",&T);
while( T--)
{
scanf("%lf",&r);//半径
scanf("%lf%lf",&px,&py);//p的坐标
scanf("%lf%lf",&qx,&qy);//q的坐标
//求出距离
pd = sqrt(px*px+py*py);//OP和OQ的长度
//这个必须要,防止有的点太小
if( fabs(pd) < eps)
{
printf("%.7f\n",2*r);
continue;
}
//比例
ratio = pd/r;
//求P的反演点
double a;
a = r*r/(pd*pd);
ppx = a*px;
ppy = a*py;
//求Q的反演点
qqx = a*1.0*qx;
qqy = a*1.0*qy;
//反演点的中点
fx = (ppx+qqx)/2;
fy = (ppy+qqy)/2;
//反演点到原点的距离
fd = sqrt(fx*fx+fy*fy);
ans = 0;
if( fd <= r)//有交点,最小距离正比于就为两反演点长度
ans = dist(ppx,ppy,qqx,qqy)*ratio;
else//无交点,找出反演线的中垂线与圆的交点
{
double k = r/fd;
double xx = fx*k;//交点
double yy = fy*k;
ans = 2*sqrt((xx-px)*(xx-px)+(yy-py)*(yy-py));
}
printf("%.7f\n",ans);
}
return 0;
}