最小圆覆盖(随机增量法)
原理:
设前k个点的最小覆盖圆为Ck
在前i-1个点的最小覆盖圆C(i-1)的基础上
Ⅰ 第i个点不被C(i-1)覆盖,则点i一定在Ci上,做固定了点i、前i个点的最小圆覆盖
初始让Ci的圆心为点i,0为半径,然后逐步把前i-1个点加到Ci内
枚举点j,j∈[1,i-1]
1.点j不被Ci覆盖,则构建一个新的Cj,点i和点j一定在Cj上,做固定了点i j,前j个点的最小圆覆盖
初始让Cj的圆心为i和j的中点,i和j距离的一半为半径,然后逐步把前j-1个点加到Cj内
枚举点k,k∈[1,j-1]
a. 点k不被Cj覆盖,则构建一个新的Ck,点i,j,k一定可以构成一个圆,此时的圆覆盖了i j 和 前k个点
b. 点k被Cj覆盖,Cj不变
2.点j被Ci覆盖,Ci不变
Ⅱ 第i个点被C(i-1)覆盖,Ci=C(i-1)
时间复杂度:
假设随机产生n个点,最小覆盖圆是C。因为三点共圆,当第n个点落在C的那3个点上时,说明C是重构的。
所以随机生成n个点,最后一个点不在前n-1个点的最小覆盖圆上的概率为3/n
总复杂度为线性的
要把原先点的顺序随机打乱
如果ijk三点幸运的共线了,可以取他们较远的两个点的中点为圆心
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define N 1000001 struct Point { double x,y; }e[N]; struct Circle { Point c; double r; }a; double dis(Point a,Point b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } Circle mid(Point a,Point b) { Circle o; o.c.x=(a.x+b.x)/2; o.c.y=(a.y+b.y)/2; o.r=dis(a,b)/2; return o; } Circle work(Point A,Point B,Point C) { double a=B.x-A.x; double b=C.x-A.x; double c=B.y-A.y; double d=C.y-A.y; double e=(B.x*B.x-A.x*A.x+B.y*B.y-A.y*A.y)/2; double f=(C.x*C.x-A.x*A.x+C.y*C.y-A.y*A.y)/2; Circle cc; double m=a*d-b*c; if(abs(m)<1e-4) { double dis1=dis(A,C); double dis2=dis(B,C); if(dis1>dis2) return mid(A,C); return mid(B,C); } cc.c.x=(e*d-c*f)/m; cc.c.y=(a*f-b*e)/m; cc.r=dis(cc.c,A); return cc; } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%lf%lf",&e[i].x,&e[i].y); random_shuffle(e+1,e+n+1); a.c=e[1]; for(int i=2;i<=n;++i) if(dis(e[i],a.c)>a.r) { a.c=e[i]; a.r=0; for(int j=1;j<i;++j) if(dis(e[j],a.c)>a.r) { a=mid(e[i],e[j]); for(int k=1;k<j;++k) { if(dis(e[k],a.c)>a.r) a=work(e[i],e[j],e[k]); // printf("i=%d j=%d k=%d : %.2lf %.2lf %.2lf \n",i,j,k,a.c.x,a.c.y,a.r); } } } printf("%.2lf %.2lf %.2lf",a.c.x,a.c.y,a.r); }