HDU6097 Mindis

题目链接: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 }

猜你喜欢

转载自www.cnblogs.com/Blogggggg/p/9277939.html
hdu
今日推荐