CF 2C Commentator problem

題目大意:在平面上有三個沒有公共部分的圓,求平面上一點使得到三個圓的切線的夾角相等。

題解:根據題意易知,要求的這個點和每個圓的圓心以及切點構成的三角形是相似的。因此該點是以三個圓心爲圓心,半徑之比等於三個圓半徑之比的三個圓的交點。可以先求兩個圓的交點,再看這兩個點和第三個圓的關係,因爲這個關係是單調的,所以可以二分三個圓的半徑,使得它們恰好交於一點。
求圓的交點可以直接解方程,比較繁瑣,這裏可以用向量法來解,但是要分幾種情況討論。
設圓心距爲O0O1
情況1 max(r1,r2)O0O1r1+r2

圖1
首先可以求出BO1O0 ,令其等於θ ,可求cosθ=r21+O0O21r222r1O0O1 ,則QB=r1sinθ ,O0QO1Q 可求,Q 座標可由圓心座標推出,ABO0O1 垂直平分,因而AB 座標可由Q 推出。

情況2 0O0O1<max(r1,r2)
這種情況和上面類似,只是向量O0QO0O1 反向。

情況3 r1+r2<O0O1O0O1+min(r1,r2)<max(r1,r2)
此時無交點。

精度是深坑…感覺用了一坨數值函數分各種情況討論還不如解方程…
在判是否在圓上的時候,可以用相對誤差來判斷,可能會好一些。

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章