链接:http://acm.hdu.edu.cn/showproblem.php?pid=6097
分析:考虑P关于圆O的反演点P',对于圆上任意一点M,|PM|/|P'M|=|OP|/r,然后就把问题转化为圆外两点到圆上动点距离之和最小了,因为|OP|=|OQ|,线段P'Q'要么和圆有交点,要么所在直线与圆都没有交点,前一种情况,显然|P'M|+|Q'M|>=|P'Q'|,等号当M在P'Q'上时取得,后一种情况,作与P'Q'平行的切线,切点就是M,由于|OP|=|OQ|,M也就是PQ中垂线与圆的交点,勾股定理一下就行了。P' Q'并不需要求出来,利用相似,算△OPQ就可以了,再乘个比例。最后还要注意PQ重合的情况。。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 int xx1,yy1,xx2,yy2,r; 7 inline double ABS(double x){ 8 if(x<0)return -x; 9 return x; 10 } 11 double dis(int x1,int y1,int x2,int y2){ 12 return sqrt(1.0*((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))); 13 } 14 double solve(){ 15 if(xx1==xx2&&yy1==yy2){ 16 return 2.0*r-2.0*dis(xx1,yy1,0,0); 17 } 18 double op=dis(xx1,yy1,0,0),pq=dis(xx1,yy1,xx2,yy2),d; 19 if(xx1==xx2) 20 d=(xx1>=0?xx1:-xx1); 21 else if(yy1==yy2) 22 d=(yy1>=0)?yy1:-yy1; 23 else 24 d=ABS(xx1*yy2-yy1*xx2)/dis(xx1,yy1,xx2,yy2); 25 d=d*r*r/(op*op); 26 if(d<=r){ 27 double q=2*sqrt(r*r+op*op-2.0*r*sqrt(op*op-(pq*pq)/4)); 28 return r*pq/op; 29 }else{ 30 return 2*sqrt(r*r+op*op-2.0*r*sqrt(op*op-(pq*pq)/4)); 31 } 32 } 33 int main(){ 34 //freopen("e:\in.txt","r",stdin); 35 int t; 36 scanf("%d",&t); 37 while(t--){ 38 scanf("%d%d%d%d%d",&r,&xx1,&yy1,&xx2,&yy2); 39 printf("%.8f ",solve()); 40 } 41 return 0; 42 }