题目大意:
①给出三角形三个点,求三角形外接圆,求外接圆的圆心和半径。
②给出三角形三个点,求三角形内接圆,求内接圆的圆心和半径。
③给出一个圆,和一个点,求过该点的圆的切线与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; }
随机增量法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; }