題目大意:在平面上有三個沒有公共部分的圓,求平面上一點使得到三個圓的切線的夾角相等。
題解:根據題意易知,要求的這個點和每個圓的圓心以及切點構成的三角形是相似的。因此該點是以三個圓心爲圓心,半徑之比等於三個圓半徑之比的三個圓的交點。可以先求兩個圓的交點,再看這兩個點和第三個圓的關係,因爲這個關係是單調的,所以可以二分三個圓的半徑,使得它們恰好交於一點。
求圓的交點可以直接解方程,比較繁瑣,這裏可以用向量法來解,但是要分幾種情況討論。
設圓心距爲
情況1
首先可以求出
情況2
這種情況和上面類似,只是向量
情況3
此時無交點。
精度是深坑…感覺用了一坨數值函數分各種情況討論還不如解方程…
在判是否在圓上的時候,可以用相對誤差來判斷,可能會好一些。
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-12;
const double eps2 = 1e-5;
const double inf = 1e12;
struct Point{
double x,y;
Point(double x,double y):x(x),y(y){}
Point(){}
double length2(){
return x*x+y*y;
}
double length(){
return sqrt(length2());
}
Point operator-(const Point &a)const{
return Point(x-a.x,y-a.y);
}
Point operator+(const Point &a)const{
return Point(x+a.x,y+a.y);
}
Point operator*(const double a)const{
return Point(x*a,y*a);
}
Point unity(){
Point ret;
double l = length();
ret.x =x/l;
ret.y =y/l;
return ret;
}
};
struct Cir{
double x,y,r;
bool operator<(const Cir &a)const{
return r<a.r;
}
bool judge(const Point &a){
double xx,yy;
xx = a.x-x;
yy = a.y-y;
double tmp = xx*xx+yy*yy;
double rr = r*r;
//printf("xx=%f yy=%f tmp=%f rr=%f eps = %f eps2=%f\n",xx,yy,tmp,rr,fabs(tmp-rr),tmp/rr);
if(fabs(tmp/rr-1)<eps2) return true;
return false;
}
bool in(const Point &a){
if((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y)-r*r<0) return true;
return false;
}
};
Cir c[3];
Point Ver(const Point &a){
Point ret;
if(fabs(a.x)<eps){
ret.x = 1;
ret.y = 0;
}
else{
ret.y = 1;
ret.x = -a.y/a.x;
}
ret = ret.unity();
return ret;
}
int main(){
for(int i = 0;i < 3;i++){
scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].r);
}
//for(int i = 0;i < 3;i++){
// printf("O%d=%f,%f\n",i,c[i].x,c[i].y);
//}
sort(c,c+3);
double L,R,mid;
L = c[0].r;
R = 2000;
c[1].r /= c[0].r;
c[2].r /= c[0].r;
c[0].r = 1;
mid = (L+R)/2;
Point OO1 = Point(c[1].x-c[0].x,c[1].y-c[0].y);
Point O0 = Point(c[0].x,c[0].y);
//printf("OO1=%f,%f e(OO1)=%f,%f\n",OO1.x,OO1.y,OO1.unity().x,OO1.unity().y);
Point p1,p2;
//printf("L %f R %f\n",L,R);
int cnt = 1;
Cir cc = c[2];
while(R-L>eps){
double r[3];
for(int i = 0;i < 3;i++) r[i] = c[i].r * mid;
cc.r = r[2];
if(r[0]+r[1]<OO1.length()){
L = mid;
mid = (L+R)/2;
//printf("i=%d L=%f R=%f mid=%f\n\n",cnt++,L,R,mid);
continue;
}
else if(r[0]+OO1.length()<r[1]){
R = mid;
mid = (L+R)/2;
//printf("i=%d L=%f R=%f mid=%f\n\n",cnt++,L,R,mid);
continue;
}
double cossita = (r[1]*r[1]+OO1.length2()-r[0]*r[0])/(2*r[1]*OO1.length());
int flag;
if(OO1.length2()+r[0]*r[0]<r[1]*r[1]) flag = -1;
else flag = 1;
//printf("sin(sita)=%f cos(sita)=%f\n",sin(sita),cos(sita));
Point Q = O0 + OO1.unity()*sqrt(r[0]*r[0]-r[1]*r[1]*(1-cossita*cossita))*flag;
//printf("Q=%f,%f\n",Q.x,Q.y);
Point v = Ver(OO1)*r[1]*sin(acos(cossita));
p1 = Q+v;
p2 = Q-v;
if(cc.in(p1)||cc.in(p2)){
R = mid;
mid = (L+R)/2;
//printf("i=%d L=%f R=%f mid=%f p1=%f,%f p2=%f,%f\n\n",cnt++,L,R,mid,p1.x,p1.y,p2.x,p2.y);
}
else{
L = mid;
mid = (L+R)/2;
//printf("i=%d L=%f R=%f mid=%f p1=%f,%f p2=%f,%f\n\n",cnt++,L,R,mid,p1.x,p1.y,p2.x,p2.y);
}
}
//printf("mid %f\n",mid);
//printf("cc=%f,%f,%f\n",cc.x,cc.y,cc.r);
//printf("p1=%f,%f p2=%f,%f\n",p1.x,p1.y,p2.x,p2.y);
if(cc.judge(p1)){
printf("%.5f %.5f\n",p1.x,p1.y);
return 0;
}
else if(cc.judge(p2)){
printf("%.5f %.5f\n",p2.x,p2.y);
return 0;
}
return 0;
}