最小圆覆盖

XXI.最小圆覆盖

随机增量法。

引理1.对于任意一组点集S和某点P,则P要么在S的外接圆内,要么在SP的外接圆上。

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

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

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

我们考虑最后枚举一个1j1的变量k,重复上述操作。如果还不在的话,则我们已经知道了i,j,k三点,三点确定一个圆。于是我们就可以直接解出该圆。

具体而言,我们作出三点间两对点的中垂线,然后求出两条中垂线的交点即可。中垂线可以简单由两点的中点和两点间向量旋转90得到。

下面是复杂度分析。可以使用主定理证明,若我们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;
}

posted @   Troverld  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示