题目链接:https://cn.vjudge.net/problem/HDU-6097
知识点: 计算几何、圆的反演
题目大意:
已知一个圆心在原点的圆的半径,再给定 \(P, Q\) 两点坐标( \(PO=QO\),\(P, Q\) 不在圆外),在圆上取一点 \(D\),求 \(PD+QD\) 的最小值。
解题思路:
首先,\(P, Q\) 两点重合的情况要特判;
其次,\(P, Q\) 在圆上的情况也要特判(将 \(D\) 点放在 \(P\) 或 \(Q\) 点上即可,答案为 \(|PQ|\) );
最后一种情况:
先介绍一下圆的反演这个概念:
圆的反演就是对于圆\(O\)所在平面的任意一点 \(S\) ,找到一个点 \(S'\) ,使得满足以下条件:
\(1)\) \(S'\) 在直线 \(OS\) 上;
\(2)\) \(|OS||OS'| = R^2\),\(R\) 为圆的半径(由这个条件可以利用相似三角形的性质推导出反演的一个性质:对于圆上任意一点 \(P\) ,有 \(\triangle POS \sim \triangle S'OP\));
圆的反演点还有一个重要的性质:圆内的反演点在圆外,圆外的反演点在圆内,圆上的反演点是它本身。
具体可以看看这篇文章。
先求出 \(P, Q\) 的反演点 \(P', Q'\),由反演的性质易知 \(\triangle P'OD \sim \triangle DOP\),\(PD = PO*P'D/r\),同理有 \(QD = QO*Q'D/r\),则 \(PD+QD = PO/r(P'D+Q'D)\)。故我们只需求出 \(P'D+Q'D\) 的最小值即可,因为圆内的反演点在圆外,而圆外的情况是比较容易处理的。
如果 \(P'Q'\) 与圆有交点,则 \(P'D+Q'D\) 的最小值即为 \(|P'Q'|\);否则 \(D\) 取 \(P'Q'\) 的中垂线与圆的交点。
具体细节请看代码。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const double eps=1e-10; 4 int dcmp(double x){ 5 if(fabs(x)<eps) return 0; 6 else return x<0?-1:1; 7 } 8 struct Point{ 9 double x,y; 10 Point(double x=0,double y=0):x(x),y(y){} 11 };typedef Point Vector; 12 Vector operator +(Vector A,Vector B){ 13 return Vector(A.x+B.x,A.y+B.y); 14 } 15 Vector operator -(Point A,Point B){ 16 return Vector(A.x-B.x,A.y-B.y); 17 } 18 Vector operator *(double p,Vector A){ 19 return Vector(A.x*p,A.y*p); 20 } 21 double Dis(Point A,Point B){//求两点距离 22 return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y)); 23 } 24 double Dot(Vector A,Vector B){//求向量点积 25 return A.x*B.x+A.y*B.y; 26 } 27 double Length(Vector A){ 28 return sqrt(Dot(A,A)); 29 } 30 double Cross(Vector A,Vector B){ 31 return A.x*B.y-A.y*B.x; 32 } 33 Point O;//坐标原点 34 35 Point Inver(Point a,double r){//求反演点 36 double dis=sqrt(a.x*a.x+a.y*a.y); 37 double t=r*r/dis/dis; 38 return Point(t*a.x,t*a.y); 39 } 40 double DistanceToLine(Point P,Point A,Point B){//求直线AB到点P的距离 41 Vector v1=B-A,v2=P-A; 42 return fabs(Cross(v1,v2))/Length(v1); 43 } 44 double cal(Point A,Point B,double r){//求直线AB的中垂线与圆的交点D,并返回|AD|+|BD| 45 double dis=Dis(A,O); 46 double ang=acos(Dot(A,B)/(dis*dis))/2.0;//利用cos<v1,v2> = v1*v2/(|v1||v2|)这条性质求两个向量的夹角的一半 47 return 2.0*sqrt(r*r+dis*dis-2*r*dis*cos(ang)); 48 } 49 50 int main(){ 51 // freopen("in.txt","r",stdin); 52 int T; 53 int r1,xq1,yq1,xp1,yp1; 54 Point P,Q,rP,rQ; 55 scanf("%d",&T); 56 while(T--){ 57 scanf("%d",&r1); 58 scanf("%d%d%d%d",&xp1,&yp1,&xq1,&yq1); 59 P=Point((double)xp1,(double)yp1),Q=Point((double)xq1,(double)yq1); 60 double r=(double)r1; 61 if(xq1==xp1&&yq1==yp1){//特判1 62 printf("%.7lf\n",2.0*(r-Dis(P,O))); 63 continue; 64 } 65 if(xp1*xp1+yp1*yp1==r1*r1){//特判2 66 printf("%.7lf\n",Dis(P,Q)); 67 continue; 68 } 69 rP=Inver(P,r),rQ=Inver(Q,r); 70 double d=DistanceToLine(O,rP,rQ); 71 72 if(dcmp(d-r)>0) 73 printf("%.7lf\n",cal(P,Q,r)); 74 else 75 printf("%.7lf\n",Dis(P,O)/r*Dis(rP,rQ)); 76 } 77 return 0; 78 }