圓的反演變換(HDU4773)

題意:給出兩個相離的圓O1,O2和圓外一點P,求構造這樣的圓:同時與兩個圓相外切,且經過點P,輸出圓的圓心和半徑
分析:畫圖很容易看出這樣的圓要麼存在一個,要麼存在兩個:此題直接解方程是不容易的,先看看反演的定義:已知一圓C,圓心爲O,半徑爲r,如果P與P’在過圓心O的直線上,且,則稱P與P'關於O互爲反演。
反演的性質:
首先設出反演圓心O和反演半徑R
1、圓外一點P與圓內一點P‘會一一對應的反演OP*OP'=R*R
2、經過O的圓,反演後成爲不經過O的一條直線
3、不經過O的圓,反演後成爲另一個圓,且圓心並不對應
4、不經過O的直線反演後成爲一個經過O的圓
5、過O的直線反演後不變
具體參考該http://blog.csdn.net/acdreamers/article/details/16966369
注意:反演中心圓的半徑要設大一點,否則會有精度問題

代碼:
#include"stdio.h"
#include"string.h"
#include"stdlib.h"
#include"queue"
#include"algorithm"
#include"string.h"
#include"string"
#include"math.h"
#include"vector"
#include"stack"
#include"map"
#define eps 1e-4
#define inf 0x3f3f3f3f
#define M 609
#define PI acos(-1.0)
using namespace std;
struct node//二維點座標
{
    double x,y;
    node(){};
    node(double xx,double yy)
    {
        x=xx;
        y=yy;
    }
    node operator+(node b)
    {
        return node(x+b.x,y+b.y);
    }
    node operator-(node b)
    {
        return node(x-b.x,y-b.y);
    }
    node operator*(double b)
    {
        return node(x*b,y*b);
    }
    double operator*(node b)
    {
        return x*b.x+y*b.y;
    }
    double operator^(node b)
    {
        return x*b.y-y*b.x;
    }
};
struct Circle//定義圓心和半徑
{
    node center;
    double r;
}p[M];
struct Line//定義直線一般式的三個參數ABC
{
     double A,B,C;
};
struct Line2//定義兩條直線
{
    Line s,e;
};
double len(node a)//求向量的長度
{
    return sqrt(a*a);
}
double dis(node a,node b)//求兩個點的距離
{
    return len(a-b);
}
double cross(node a,node b,node c)//求叉乘
{
    return (b-a)^(c-a);
}
double dot(node a,node b,node c)//求點乘
{
    return (b-a)*(c-a);
}
Circle InverCircle(node p,double R,Circle c)//已知反演中心和反演半徑,求圓c的反形圓
{
    Circle ret;
    double pc=dis(p,c.center);
    ret.r=R*R/2*(1/(pc-c.r)-1/(pc+c.r));
    double pret=R*R/(pc+c.r)+ret.r;
    ret.center.x=p.x+(pret/pc)*(c.center.x-p.x);
    ret.center.y=p.y+(pret/pc)*(c.center.y-p.y);
    return ret;
}
node InterPoint(node p,Line L)//過直線外一點與直線L的垂足
{
    node ret;
    ret.y=(L.A*L.A*p.y-L.A*L.B*p.x-L.B*L.C)/(L.A*L.A+L.B*L.B);
    ret.x=(L.B*L.B*p.x-L.A*L.B*p.y-L.A*L.C)/(L.A*L.A+L.B*L.B);
    return ret;
}
Circle InverLine(node p,double R,Line L)//已知不過反演中心的直線,求其反形圓(反形圓過反演中心)
{
    Circle ret;
    node q=InterPoint(p,L);
    double l1=dis(p,q);
    double l2=R*R/l1;
    ret.r=l2/2;
    ret.center.x=p.x+ret.r/l1*(q.x-p.x);//利用相似三角形求解
    ret.center.y=p.y+ret.r/l1*(q.y-p.y);
    return ret;
}
Line line(node a,node b)//已知兩個不同的點,求過這兩個點的直線
{
    Line l;
    l.A=b.y-a.y;
    l.B=a.x-b.x;
    l.C=b.x*a.y-a.x*b.y;
    return l;
}
double disLL(Line L1,Line L2)//兩條平行線間垂直距離
{
    return fabs(L1.C-L2.C)/sqrt(L1.A*L1.A+L1.B*L1.B);
}
double disPL(node p,Line L)//求點到直線的距離
{
    return fabs(L.A*p.x+L.B*p.y+L.C)/sqrt(L.A*L.A+L.B*L.B);
}
node Ratate(node a,double rad)//向量逆時針旋轉rad的角度
{
    return node(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));
}
Line PointCutCircle(node p,Circle c,int clock)//過圓外一點p且與圓的切線,clock代表兩個不同的切線
{
    Line ret;
    node she;
    she=c.center-p;
    double pc=dis(c.center,p);
    double rad=asin(c.r/pc);
    if(clock==-1)//逆時針旋轉
    {
        node she1=Ratate(she,rad);
        ret.A=she1.y;
        ret.B=-she1.x;
        ret.C=she1.x*p.y-p.x*she1.y;
    }
    if(clock==1)//順時針旋轉
    {
        node she1=Ratate(she,-rad);
        ret.A=she1.y;
        ret.B=-she1.x;
        ret.C=she1.x*p.y-p.x*she1.y;
    }
    return ret;
}
Line2 CircleCutCircle(Circle O1,Circle O2)//求兩個相離的圓的兩條外公切線
{
    Line2 L;
    Line l1,l2,L1;
    if(fabs(O1.r-O2.r)<eps)//當量圓半徑相同時
    {
        L1=line(O1.center,O2.center);
        l1.A=L1.A;
        l1.B=L1.B;
        l1.C=L1.C+fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        l2.A=L1.A;
        l2.B=L1.B;
        l2.C=L1.C-fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        L.s=l1;
        L.e=l2;
    }
    else//當兩個圓的半徑不同時
    {
        if(O1.r>O2.r)
            swap(O1,O2);
        Circle O;
        O.center=O2.center;
        O.r=O2.r-O1.r;
        L1=PointCutCircle(O1.center,O,-1);
        l1.A=L1.A;
        l1.B=L1.B;
        l1.C=L1.C+fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        l2.A=L1.A;
        l2.B=L1.B;
        l2.C=L1.C-fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        if(fabs(disPL(O1.center,l1)-O1.r)<eps&&fabs(disPL(O2.center,l1)-O2.r)<eps)
            L.s=l1;
        if(fabs(disPL(O1.center,l2)-O1.r)<eps&&fabs(disPL(O2.center,l2)-O2.r)<eps)
            L.s=l2;
        L1=PointCutCircle(O1.center,O,1);
        l1.A=L1.A;
        l1.B=L1.B;
        l1.C=L1.C+fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        l2.A=L1.A;
        l2.B=L1.B;
        l2.C=L1.C-fabs(O1.r)*sqrt(L1.A*L1.A+L1.B*L1.B);
        if(fabs(disPL(O1.center,l1)-O1.r)<eps&&fabs(disPL(O2.center,l1)-O2.r)<eps)
            L.e=l1;
        if(fabs(disPL(O1.center,l2)-O1.r)<eps&&fabs(disPL(O2.center,l2)-O2.r)<eps)
            L.e=l2;
    }
    return L;
}
int main()
{
    int T,cnt;
    scanf("%d",&T);
    while(T--)
    {
        Circle C1,C2,C3,C4,C,ans[4];
        Line2 L;
        node P;
        scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&C1.center.x,&C1.center.y,&C1.r,&C2.center.x,&C2.center.y,&C2.r,&P.x,&P.y);
        C3=InverCircle(P,30.0,C1);
        C4=InverCircle(P,30.0,C2);
        L=CircleCutCircle(C3,C4);

        cnt=0;
        C=InverLine(P,30.0,L.s);
        if(fabs(dis(C.center,C1.center)-C1.r-C.r)<eps&&fabs(dis(C.center,C2.center)-C2.r-C.r)<eps)
            ans[cnt++]=C;

        C=InverLine(P,30.0,L.e);
        if(fabs(dis(C.center,C1.center)-C1.r-C.r)<eps&&fabs(dis(C.center,C2.center)-C2.r-C.r)<eps)
            ans[cnt++]=C;

        printf("%d\n",cnt);
        for(int i=0;i<cnt;i++)
            printf("%.8lf %.8lf %.8lf\n",ans[i].center.x,ans[i].center.y,ans[i].r);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章