看了题解好久,终于弄懂了,真的是菜。。。。直接上图:
过点P做P的反演点
P1
,使得
OP∗OP1=OD2=r2
则OPOD=ODOP1=DPDP1
所以只要求出 DP1 的最小值,就可以按比例求出DP。同理求出 DQ1 的最小值,就可以按比例求出DQ。也就是说题目转化成了求 DP1+DQ1 的最小值,两点之间直线最短,连接 P1Q1 ,若与圆相交,则交点即为所求D。若不与圆相交,则PQ中垂线与圆的交点就是D。
判断 P1Q1 与圆的位置关系可以直接算圆心与线段 P1Q1 的距离与半径的大小即可。
code:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string>
#include <set>
//a&3==a%4
using namespace std;
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
const double eps=1e-8;
const int maxn=30010;//须填写
const int inf=0x3f3f3f3f;
double r,x,y;
double len;
struct Point//可以表示点与向量
{
double x,y;
Point()
{
x=0;
y=0;
}
Point(double _x,double _y)
{
x=_x;
y=_y;
}
};
Point zero=Point(0.0,0.0);//(0,0)点
bool judge1(Point a,Point b)//判断两点是否重合
{
if(a.x==b.x&&a.y==b.y)
return true;
else
return false;
}
double dis(Point a,Point b)//两点间的距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double chacheng(Point a,Point b)/*两点之间叉乘,用来求三角形OP1Q1的面积,进而求O到P1Q1的距离*/
{
double res=a.x*b.y-b.x*a.y;
if(res-0>eps)
{
return res;
}
else{
return 0-res;
}
}
Point shucheng(Point a,double b)//数乘运算,用来求向量OP1,OQ1
{
Point c=Point(a.x*b,a.y*b);
return c;
}
bool judge2(Point a,Point b)
{
double bili=r*r/dis(zero,a)/dis(zero,a);
Point a1=shucheng(a,bili);
Point b1=shucheng(b,bili);
double h=chacheng(a1,b1)/dis(a1,b1);
if(h-r>eps)
return false;
else return true;
}
int main()
{
int kase;
scanf("%d",&kase);
while(kase--)
{
scanf("%lf",&r);
scanf("%lf%lf",&x,&y);
Point p=Point(x,y);
scanf("%lf%lf",&x,&y);
Point q=Point(x,y);
if(judge1(p,q))//判断两点是否重合
{
len=2*(r-dis(q,zero));
}
else if(judge2(p,q))//判断P1Q1与圆是否有交点
{//有的话求出P1Q1,然后按比例求出PQ
double bili=r*r/dis(zero,p)/dis(zero,p);
Point p1=shucheng(p,bili);
Point q1=shucheng(q,bili);
len=dis(p1,q1)*dis(p,zero)/r;
}
else{//没有的话PQ中垂线与圆的交点就是D
len=0;
Point mid=Point((p.x+q.x)/2.00,(q.y+p.y)/2.00);
double bili=r/dis(zero,mid);
mid=shucheng(mid,bili);
len=dis(mid,p)+dis(mid,q);
}
printf("%.7f\n",len);
}
return 0;
}