最小圆覆盖
XXI.最小圆覆盖
随机增量法。
引理1.对于任意一组点集和某点,则要么在的外接圆内,要么在的外接圆上。
于是我们可以设计出如下的解法:
我们枚举一个的变量i
,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的端点之一。令此时的外接圆即为当前点本身。
我们考虑再枚举一个的变量j
,并判断当前点是否在当前外接圆内。如果在的话,直接跳过;否则我们就找到了当前集合所对应的外接圆的另一个端点。令i
与j
作为此时的外接圆的直径。
我们考虑最后枚举一个的变量k
,重复上述操作。如果还不在的话,则我们已经知道了i,j,k
三点,三点确定一个圆。于是我们就可以直接解出该圆。
具体而言,我们作出三点间两对点的中垂线,然后求出两条中垂线的交点即可。中垂线可以简单由两点的中点和两点间向量旋转得到。
下面是复杂度分析。可以使用主定理证明,若我们random_shuffle
一下点集,则其期望复杂度是的。
代码:
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?