zoukankan      html  css  js  c++  java
  • UVA 12304 /// 圆的综合题 圆的模板

    题目大意:

    ①给出三角形三个点,求三角形外接圆,求外接圆的圆心和半径。

    ②给出三角形三个点,求三角形内接圆,求内接圆的圆心和半径。

    ③给出一个圆,和一个点,求过该点的圆的切线与x轴的夹角(0<=angle<180);

    ④给出一条直线,一个点p,指定半径r,求经过点p的与直线相切的半径为r的圆;

    ⑤给出两条直线,求与这两条直线相切的圆;

    ⑥给出两个圆,求同时与这两个圆相切的圆;

    贴一下圆的模板 带了一些注释

    主要包括以下内容

    // 求三角形abc的外接圆c(圆心、半径)

    C csC(P a,P b,P c)

    // 求三角形abc的内接圆c(圆心、半径)

    C isC(P a,P b,P c)

    // 直线l与圆c的交点 返回个数 交点坐标存入s

    int insLC(L l,C c,vector<P>& s)

    // 圆c1与c2的交点 返回个数 交点坐标存入s

    int insCC(C c1,C c2,vector<P>& s)

    // 过点p求与圆c相切的切线 返回条数 切线向量存入v

    int PCgetL(P p,C c,vector <P>& v)

    // 过点p求与直线l相切的半径为r的圆 返回个数 圆心坐标存入ans

    int PLgetC(P p,L l,double r,vector <P>& ans)

    // 求与直线l1和l2相切的半径为r的圆 返回个数 圆心坐标存入ans

    int LLgetC(L l1,L l2,double r,vector<P>& ans)

    // 求圆c1和c2的公切线 返回条数 切线与c1的交点存入ansa 与c2的交点存入ansb

    int CCgetL(C c1,C c2,vector <P>& ansa,vector <P>& ansb)

    // 求圆c1与c2的相交面积 (本题中无用 只是顺便贴一下)

    double insAreaCC(P c1, double r1, P c2, double r2)

    // 求n个点的最小圆覆盖(圆心、半径)

    C MCC()

    解法其实也多种多样 这里放两份题解

    https://www.cnblogs.com/dilthey/p/7758012.html

    https://blog.csdn.net/peteryuanpan/article/details/40315381

    #include <bits/stdc++.h>
    #define pb(a) push_back(a)
    #define PI acos(-1)
    using namespace std;
    
    const double eps=1e-6;
    double add(double a,double b) {
        if(abs(a+b)<eps*(abs(a)+abs(b))) return 0;
        return a+b;
    }
    struct P {
        double x,y;
        P(){}
        P(double _x,double _y):x(_x),y(_y){}
        P operator - (P p) {
            return P(add(x,-p.x),add(y,-p.y)); }
        P operator + (P p) {
            return P(add(x,p.x),add(y,p.y)); }
        P operator * (double d) {
            return P(x*d,y*d); }
        P operator / (double d) {
            return P(x/d,y/d); }
        double dot (P p) {
            return add(x*p.x,y*p.y); }
        double det (P p) {
            return add(x*p.y,-y*p.x); }
        bool operator == (const P& p)const {
            return abs(x-p.x)<eps && abs(y-p.y)<eps; }
        bool operator < (const P& p)const {
            return x<p.x || (x==p.x && y<p.y);
        }
        void read() {
            scanf("%lf%lf",&x,&y); }
    };
    struct L {
        P p, v;
        double ang;
        L(){}
        L(P _p,P _v):p(_p),v(_v){ ang=atan2(v.y,v.x); }
        P acP(double t) {
            return p+v*t;
        }
    };
    struct C {
        P p; double r;
        C(){}
        C(P _p,double _r):p(_p),r(_r){}
        P acP(double a) {
            return P(p.x+cos(a)*r,p.y+sin(a)*r);
        }
        void read() {
            scanf("%lf%lf%lf",&p.x,&p.y,&r); }
    };
    // 求向量a的长度
    double lenP(P a) {
        return sqrt(a.dot(a));
    }
    // 求向量v的垂直单位向量
    P normal(P v) {
        double len=lenP(v);
        return P(-v.y/len,v.x/len);
    }
    // 求旋转ang后的向量v
    P Rotate(P v,double ang) {
        P u=P(sin(ang),cos(ang));
        return P(v.det(u),v.dot(u));
    }
    // 求两向量夹角
    double Angle(P u,P v) {
        return acos(u.dot(v)/lenP(u)/lenP(v));
    }
    // 求向量v极角
    double angle(P v) {
        return atan2(v.y,v.x);
    }
    // 求点c到直线ab距离
    double lenPL(P a,P b,P c) {
        if(a==b) return lenP(a-c);
        if((b-a).dot(c-a)<0) return lenP(a-c);
        else if((b-a).dot(c-b)>0) return lenP(b-c);
        else return abs((c-a).det(b-a))/lenP(a-b);
    }
    // 求两直线交点
    P ins(L a,L b) {
        P v=a.p-b.p, v1=a.v, v2= b.v;
        double t=v2.det(v)/v1.det(v2);
        return a.p+v1*t;
    }
    
    /*求直线l与圆c的交点个数
    t1 t2为交点对应的圆心角
    交点存入s中
    */
    int insLC(L l,C c,vector<P>& s) {
        double ta=l.v.x, tb=l.p.x-c.p.x;
        double tc=l.v.y, td=l.p.y-c.p.y;
        double A=ta*ta+tc*tc, B=2*(ta*tb+tc*td), C=tb*tb+td*td-c.r*c.r;
        double delta=B*B-4.0*A*C;
    
        double t1,t2; // 圆心角
        if(delta<-eps) return 0; // <0无解
        else if(abs(delta)<eps) { // =0存在一个解
            t1=t2=-B/(2.0*A); s.pb(l.acP(t1));
            return 1;
        }
        else { // 两个解
            t1=(-B-sqrt(delta))/(2.0*A); s.pb(l.acP(t1));
            t2=(-B+sqrt(delta))/(2.0*A); s.pb(l.acP(t2));
            return 2;
        }
    }
    
    /* 判断两圆相交
    求圆c1与c2的交点 并用s保存交点
    */
    int insCC(C c1,C c2,vector<P>& s) {
        double d=lenP(c1.p-c2.p);
        if(abs(d)<eps) {
            if(abs(c1.r-c2.r)<eps) return -1; // 重合
            return 0; // 内含
        }
        if((c1.r+c2.r-d)<-eps) return 0; // 外离
        if(d-abs(c1.r-c2.r)<-eps) return 0; // 内离
    
        double ang=angle(c2.p-c1.p); // 向量c1c2求极角
        double da=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
        // c1与交点的向量 与 c1c2 的夹角
        P p1=c1.acP(ang-da), p2=c1.acP(ang+da); // 求得两交点
    
        s.pb(p1);
        if(p1==p2) return 1; // 同一个点
        s.pb(p2); return 2;
    }
    
    /*求三角形的外接圆(圆心、半径)
    */
    C csC(P a,P b,P c) {
        double bx=b.x-a.x, by=b.y-a.y;
        double cx=c.x-a.x, cy=c.y-a.y;
        double d=2*(cy*bx-cx*by);
        double x=(cy*(bx*bx+by*by)-by*(cx*cx+cy*cy))/d+a.x;
        double y=(bx*(cx*cx+cy*cy)-cx*(bx*bx+by*by))/d+a.y;
        P r(x,y);
        return C(r,lenP(a-r));
    }
    /*求三角形的内接圆(圆心、半径)
    */
    C isC(P a,P b,P c) {
        double d=lenP(b-c), e=lenP(c-a), f=lenP(a-b);
        P p=(a*d+b*e+c*f)/(d+e+f);
        return C(p,lenPL(a,b,p));
    }
    
    /*过定点p作圆c的切线l
    得p到圆心的向量(固定方向)求距离dist 判断距离
    旋转向量(以p点为心旋转)得到切线向量 并存入v
    */
    int PCgetL(P p,C c,vector <P>& v) {
        P u=c.p-p; // p到圆心的向量
        double dist=lenP(u);
        if(dist-c.r<-eps) return 0; // p在圆内
        else if(abs(dist-c.r)<eps) { // p在圆上
            v.pb(Rotate(u,PI/2)); return 1;
        }
        else {
            double ang=asin(c.r/dist); // 切线与u的夹角
            v.pb(Rotate(u,-ang));
            v.pb(Rotate(u,ang));
            return 2;
        }
    }
    
    /*过点p与直线l相切的半径为r的圆
    以点p为圆心r为半径得到圆pc
    直线l向上和向下平移r后得到lup和ldw
    得到圆pc与直线lup和ldw的交点 就是圆心 并存入ans
    */
    int PLgetC(P p,L l,double r,vector <P>& ans) {
        P v=normal(l.v); v=v/lenP(v)*r; 
        L lup(l.p+v,l.v), ldw(l.p-v,l.v); 
        int c1=insLC(lup,C(p,r),ans); 
        int c2=insLC(ldw,C(p,r),ans); 
        sort(ans.begin(),ans.end());
        return c1+c2;
    }
    
    /*与两条相交直线相切的半径为r的圆
    直线向上和向下平移r后得到lup和ldw
    每两条直线的交点即为圆心 并存入ans中
    */
    int LLgetC(L l1,L l2,double r,vector<P>& ans) {
        P v1=normal(l1.v), v2=normal(l2.v);
        v1=v1/lenP(v1)*r, v2=v2/lenP(v2)*r;
        L l1up(l1.p+v1,l1.v), l1dw(l1.p-v1,l1.v);
        L l2up(l2.p+v2,l2.v), l2dw(l2.p-v2,l2.v);
    
        ans.pb(ins(l1up,l2up));
        ans.pb(ins(l1up,l2dw));
        ans.pb(ins(l1dw,l2up));
        ans.pb(ins(l1dw,l2dw));
        sort(ans.begin(),ans.end());
        return ans.size();
    }
    
    /*求圆c1与c2的公切线
    重合无数条 内离则没有
    内切有一条 相交有两条
    外切有三条 外离有四条
    返回切线的数量 切点存入
    */
    int CCgetL(C c1,C c2,vector <P>& ansa,vector <P>& ansb) {
        if(c1.r<c2.r) swap(c1,c2), swap(ansa,ansb);
        P v=c2.p-c1.p;
        double d2=(v).dot(v); // 圆心距的平方
        double base=angle(v);
        double rd=c1.r-c2.r; // 内切时的圆心距
        double rs=c1.r+c2.r; // 外切时的圆心距
    
        if(d2<rd*rd) return 0; // 内离
        if(d2==0 && c1.r==c2.r) return -1; // 重合
        if(d2==rd*rd) {
            ansa.pb(c1.acP(base)), ansb.pb(c2.acP(base));
            return 1;
        } // 内切
    
        double ang=acos(rd/sqrt(d2)); // 外公切线与两圆心连线的夹角
        ansa.pb(c1.acP(base+ang)); ansb.pb(c2.acP(base+ang));
        ansa.pb(c1.acP(base-ang)); ansb.pb(c2.acP(base-ang));
        // 两条外切线
    
        if(d2==rs*rs) {
            ansa.pb(c1.acP(base)); ansb.pb(c2.acP(base));
        } // 外切
        else if(d2>rs*rs) {
            ang=acos(rs/sqrt(d2)); // 内公切线与两圆心连线的夹角
            ansa.pb(c1.acP(base+ang)); ansb.pb(c2.acP(base+ang));
            ansa.pb(c1.acP(base+ang)); ansb.pb(c2.acP(base+ang));
        } // 外离
    
        return ansa.size();
    }
    
    /*求圆c1与c2的面积交
    S扇形 = r*r*ang  = r*l (l为弧长 l=ang*r)
    S三角形 = r*r*sin(ang)*cos = r*r*0.5*sin(2*ang) = 0.5*r*h
    S = S扇形 - S三角形 (对应圆弧的半边)
    */
    double insAreaCC(P c1, double r1, P c2, double r2) {
        double a = lenP(c1-c2), b = r1, c = r2;
        double minr = min(r1,r2);
        if(r1+r2-a<eps) return 0; // 两圆相离
        if(a-abs(r1-r2)<eps || abs(a)<eps) 
            return PI*minr*minr; // 两圆包含
    
        double cosA=(a * a + b * b - c * c) / 2.0 / (a * b);
        double cosB=(a * a + c * c - b * b) / 2.0 / (a * c);
        double angA = acos(cosA), angB = acos(cosB);
    
        double s1 = r1*r1*angA - r1*r1*sin(angA)*cosA;
        double s2 = r2*r2*angB - r2*r2*sin(angB)*cosB;
    
        return s1 + s2;
    }
    
    /**********************************/
    
    void ops3(P p,C c) {
        vector <P> Lp; Lp.clear();
        int m=PCgetL(p,c,Lp); /// 得到切线向量
        vector <double> ans; ans.clear();
        for(int i=0;i<m;i++) {
            double ang=angle(Lp[i]); /// 切线向量转为极角
            if(ang<0) ang+=PI;
            if(abs(ang-PI)<eps) ang-=PI;
            ans.pb(ang);
        }
        sort(ans.begin(),ans.end());
        printf("[");
        for(int i=0;i<m;i++){
            if(i!=0) printf(",");
            printf("%.6f",ans[i]*180/PI); /// 极角转为角度
        }
        printf("]
    ");
    }
    void ops4(P p,L l,double r) {
        vector <P> ans; ans.clear();
        int m=PLgetC(p,l,r,ans);
        printf("[");
        for(int i=0;i<m;i++)
        {
            if(i!=0) printf(",");
            printf("(%.6lf,%.6lf)",ans[i].x,ans[i].y);
        }
        printf("]
    ");
    }
    void ops5(L l1,L l2,double r) {
        vector <P> ans; ans.clear();
        int m=LLgetC(l1,l2,r,ans);
        printf("[");
        for(int i=0;i<m;i++) {
            if(i!=0) printf(",");
            printf("(%.6lf,%.6lf)",ans[i].x,ans[i].y);
        }
        printf("]
    ");
    }
    void ops6(C c1,C c2,double r)
    {
        c1.r+=r, c2.r+=r;
        vector <P> ans; ans.clear();
        insCC(c1,c2,ans);
        sort(ans.begin(),ans.end());
        printf("[");
        for(int i=0;i<ans.size();i++) {
            if(i!=0) printf(",");
            printf("(%.6lf,%.6lf)",ans[i].x,ans[i].y);
        }
        printf("]
    ");
    }
    
    int main()
    {
        string op;
        while(cin>>op) {
            if(op=="CircumscribedCircle") {
                P p1,p2,p3;
                p1.read(), p2.read(), p3.read();
                C c=csC(p1,p2,p3);
                printf("(%.6f,%.6f,%.6f)
    ",c.p.x,c.p.y,c.r);
            }
            else if(op=="InscribedCircle") {
                P p1,p2,p3;
                p1.read(), p2.read(), p3.read();
                C c=isC(p1,p2,p3);
                printf("(%.6f,%.6f,%.6f)
    ",c.p.x,c.p.y,c.r);
            }
            else if(op=="TangentLineThroughPoint") {
                C c; c.read();
                P p; p.read();
                ops3(p,c);
            }
            else if(op=="CircleThroughAPointAndTangentToALineWithRadius") {
                P p; P a,b;
                p.read(), a.read(), b.read();
                double r; scanf("%lf",&r);
                ops4(p,L(a,b-a),r);
            }
            else if(op=="CircleTangentToTwoLinesWithRadius") {
                P a,b; P c,d;
                a.read(), b.read();
                c.read(), d.read();
                double r; scanf("%lf",&r);
                ops5(L(a,b-a),L(c,c-d),r);
            }
            else { //if(op=="CircleTangentToTwoDisjointCirclesWithRadius") {
                C c1, c2;
                c1.read(), c2.read();
                double r; scanf("%lf",&r);
                ops6(c1,c2,r);
            }
        }
    
        return 0;
    }
    View Code

    随机增量法O(n)求最小圆覆盖

    https://www.luogu.org/problemnew/solution/P1742

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    #define INF 0x3f3f3f3f
    #define LLINF 0x3f3f3f3f3f3f3f3fLL
    #define inc(i,j,k) for(int i=j;i<=k;i++)
    #define dec(i,j,k) for(int i=j;i>=k;i--)
    #define mem(i,j) memset(i,j,sizeof(i))
    #define bug(args...) cout<<#args<<"="<<args<<endl;
    const int N=1e5+5;
    const double eps=1e-8;
    
    struct P {
        double x,y;
        P(){} P(double x,double y):x(x),y(y){}
        P operator - (P p) { return P(x-p.x,y-p.y); }
        P operator + (P p) { return P(x+p.x,y+p.y); }
        P operator / (double d) { return P(x/d,y/d); }
        P operator * (double d) { return P(x*d,y*d); }
        double dot(P p) { return x*p.x+y*p.y; }
        double det(P p) { return x*p.y-y*p.x; }
    };
    struct C {
        P p; double r;
        C(){} C(P p,double r):p(p),r(r){}
    };
    
    double len(P p) { return sqrt(p.dot(p)); }
    double len2(P p) { return p.dot(p); }
    /*求三角形的外接圆(圆心、半径)*/
    C csC(P a,P b,P c) {
        double bx=b.x-a.x, by=b.y-a.y;
        double cx=c.x-a.x, cy=c.y-a.y;
        double d=2*(cy*bx-cx*by);
        double x=(cy*(bx*bx+by*by)-by*(cx*cx+cy*cy))/d+a.x;
        double y=(bx*(cx*cx+cy*cy)-cx*(bx*bx+by*by))/d+a.y;
        P r(x,y);
        return C(r,len(a-r));
    }
    /*求n个点的最小圆覆盖(圆心、半径)*/
    C MCC() {
        C c=C(P(0.0,0.0),0.0);
        inc(i,1,n) if(len2(p[i]-c.p)>c.r) {
            c=C(p[i],0.0);
            inc(j,1,i-1) if(len2(p[j]-c.p)>c.r) {
                c.p=(p[i]+p[j])/2,c.r=len2(p[j]-c.p);
                inc(k,1,j-1) if(len2(p[k]-c.p)>c.r)
                    c=csC(p[i],p[j],p[k]), c.r=len2(p[k]-c.p);
            }
        }
        c.r=sqrt(c.r);
        return c;
    }
    
    int n;
    P p[N];
    
    int main()
    {
        while(~scanf("%d",&n)) {
            inc(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y);
            random_shuffle(p+1,p+1+n);
            C c=MCC();
            printf("%.10f
    %.10f %.10f
    ",c.r,c.p.x,c.p.y);
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    morning
    周末,又见周末
    One Care, still Care
    Linux 下挂载硬盘的 方法
    Oracle 11g Alert log 文件位置的问题
    Oracle中 drop user 和 drop user cascade 的区别
    如何加快建 index 索引 的时间
    Oracle ADDM 自动诊断监视工具 介绍
    Vmware SERVER 简介
    Oracle Logminer 说明
  • 原文地址:https://www.cnblogs.com/zquzjx/p/9743910.html
Copyright © 2011-2022 走看看