题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097
题目大意:给你圆内两个点P、Q,求圆上一点D,使得 |QD|+|PD|最短
在此之前我们先了解一下反演点的知识,参照百度百科:
一般指二维反演中的点。
二维上反演以一个特定的反演圆为基础:圆心O为反演中心,圆半径为常数k,把点P反演为点P'就是使得OP×OP'=r^2.
如点P在圆外可这样作:过点P作圆的切线(两条),两个切点相连与OP连线交点就是点P'.
如点P在圆内就把这一过程反过来即可:连结OP,过点P作直线垂直于OP,直线与圆的交点处的切线的交点就是点P'.
如点P在圆上,反演后仍是它自身.按上述方法都可用尺规作图完成.
题目思路:
那么可以知道因为OP*OP'=r*r那么易证明对于圆上任意一点T,▲OP'T与▲OTP相似 (因为两对边比例相等且夹角相等都为∠POT)
知道这个结论之后,我们求的 min|QD|+|PD| 可以转化为求 min|Q'D|+|P'D| 然后再按边的比例即可得到解
如果直线P'Q'与圆没有交点,那么答案为PQ中垂线与圆的交点。
否则答案为|P'Q'|
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
struct point
{
double x,y;
};
double distance1(point p1,point p2)//求两点的距离
{
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
point intersection(point u1,point u2,point v1,point v2)//求两条直线交点
{
point ret=u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))/((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x+=(u2.x-u1.x)*t;
ret.y+=(u2.y-u1.y)*t;
return ret;
}
void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2)//求直线与圆交点
{
point p=c;
double t;
p.x+=l1.y-l2.y;
p.y+=l2.x-l1.x;
p=intersection(p,c,l1,l2);
double temp=r*r-distance1(p,c)*distance1(p,c);
t=sqrt(temp)/distance1(l1,l2);
p1.x=p.x+(l2.x-l1.x)*t;
p1.y=p.y+(l2.y-l1.y)*t;
p2.x=p.x-(l2.x-l1.x)*t;
p2.y=p.y-(l2.y-l1.y)*t;
}
double ptoline(point p,point l1,point l2)
{
point midp;
midp.x=(l1.x+l2.x)/2.0;
midp.y=(l1.y+l2.y)/2.0;
return distance1(p,midp);
}
point invpoint(point p,double k)//求点p对于原点O的反演点
{
//反演点为向量x伸长 k倍
point invp;
invp.x=p.x*k;
invp.y=p.y*k;
return invp;
}
double solve()
{
double r,ans;
point O,P,Q,invP,invQ,PQmid;
scanf("%lf",&r);
scanf("%lf%lf%lf%lf",&P.x,&P.y,&Q.x,&Q.y);
O.x=O.y=0;//原点O为圆心
PQmid.x=(P.x+Q.x)/2.0;
PQmid.y=(P.y+Q.y)/2.0;
double k=(r*r/distance1(P,O)/distance1(P,O));
invP=invpoint(P,k);
invQ=invpoint(Q,k);
if(distance1(P,O)<eps) ans=2.0*r;
else if(r<ptoline(O,invP,invQ)) //两个反演点的连线在圆外,则答案为垂直平分线与圆的交点
{
point p1,p2;
intersection_line_circle(O,r,O,PQmid,p1,p2);//求直线与圆交点
ans=min(distance1(P,p1)+distance1(Q,p1),distance1(P,p2)+distance1(Q,p2));
}
else
{
//cout<<"反演点"<<endl;
ans=distance1(invP,invQ);
ans*=distance1(P,O)/r;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--) printf("%.8lf\n",solve());
}