最小圆覆盖

XXI.最小圆覆盖

随机增量法。

引理1.对于任意一组点集\(\mathbb{S}\)和某点\(P\),则\(P\)要么在\(\mathbb{S}\)的外接圆内,要么在\({\mathbb{S}\cup P}\)的外接圆上。

于是我们可以设计出如下的解法:

我们枚举一个\(1\sim n\)的变量i,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的端点之一。令此时的外接圆即为当前点本身。

我们考虑再枚举一个\(1\sim i-1\)的变量j,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的另一个端点。令ij作为此时的外接圆的直径。

我们考虑最后枚举一个\(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;
}

原文地址:https://www.cnblogs.com/Troverld/p/14619394.html