XXI.最小圆覆盖
随机增量法。
引理1.对于任意一组点集\(\mathbb{S}\)和某点\(P\),则\(P\)要么在\(\mathbb{S}\)的外接圆内,要么在\({\mathbb{S}\cup P}\)的外接圆上。
于是我们可以设计出如下的解法:
我们枚举一个\(1\sim n\)的变量i
,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的端点之一。令此时的外接圆即为当前点本身。
我们考虑再枚举一个\(1\sim i-1\)的变量j
,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的另一个端点。令i
与j
作为此时的外接圆的直径。
我们考虑最后枚举一个\(1\sim j-1\)的变量k
,重复上述操作。如果还不在的话,则我们已经知道了i,j,k
三点,三点确定一个圆。于是我们就可以直接解出该圆。
具体而言,我们作出三点间两对点的中垂线,然后求出两条中垂线的交点即可。中垂线可以简单由两点的中点和两点间向量旋转\(90^\circ\)得到。
下面是复杂度分析。可以使用主定理证明,若我们random_shuffle
一下点集,则其期望复杂度是\(O(n)\)的。
代码:
#include<bits/stdc++.h>
using namespace std;
#define double long double
int n;
struct Vector{
double x,y;
Vector(){}
Vector(double X,double Y){x=X,y=Y;}
friend Vector operator +(const Vector &u,const Vector &v){return Vector(u.x+v.x,u.y+v.y);}
friend Vector operator -(const Vector &u,const Vector &v){return Vector(u.x-v.x,u.y-v.y);}
friend Vector operator *(const Vector &u,const double &v){return Vector(u.x*v,u.y*v);}
friend Vector operator /(const Vector &u,const double &v){return Vector(u.x/v,u.y/v);}
friend double operator &(const Vector &u,const Vector &v){return u.x*v.y-u.y*v.x;}//cross times
friend double operator |(const Vector &u,const Vector &v){return u.x*v.x+u.y*v.y;}//point times
double operator ~()const{return sqrt(x*x+y*y);}//the modulo of a vector
double operator !()const{return atan2(y,x);}//the angle of a vector
void read(){scanf("%Lf%Lf",&x,&y);}
void print(){printf("(%Lf,%Lf)",x,y);}
}p[100100];
typedef Vector Point;
struct Line{
Point x,y;
Vector z;
Line(){}
Line(Point X,Point Y){x=X,y=Y,z=Y-X;}
friend Point operator &(const Line &u,const Line &v){return u.x+u.z*((v.z&(u.x-v.x))/(u.z&v.z));}
};
typedef Line Segment;
struct Circle{
Point o;
double r;
Circle(){}
Circle(Point O,double R=0){o=O,r=R;}
Circle(Point X,Point Y){o=(X+Y)/2,r=~(o-X);}
Circle(Point X,Point Y,Point Z){
Line u=Line((X+Y)/2,(X+Y)/2+Vector(Y.y-X.y,X.x-Y.x));
Line v=Line((Y+Z)/2,(Y+Z)/2+Vector(Z.y-Y.y,Y.x-Z.x));
o=u&v;
r=~(o-Y);
}
friend bool operator &(const Circle &u,const Point &v){return ~(u.o-v)<=u.r;}
};
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)p[i].read();
random_shuffle(p+1,p+n+1);
Circle O=Circle(p[1]);
for(int i=2;i<=n;i++){
if(O&p[i])continue;
O=Circle(p[i]);
for(int j=1;j<i;j++){
if(O&p[j])continue;
O=Circle(p[i],p[j]);
for(int k=1;k<j;k++)if(!(O&p[k]))O=Circle(p[i],p[j],p[k]);
}
}
printf("%.10Lf\n%.10Lf %.10Lf\n",O.r,O.o.x,O.o.y);
return 0;
}