题解 P1742 【最小圆覆盖】
计算几何
前置知识:外接圆的求法
在初中我们就学过三点确定一个圆
这里我们首先考虑一下怎么求三角形的外接圆
我们考虑暴力解方程即可
利用圆的方程
我们将三个点分别代进去可以得到三个方程
然后乱搞一下就能把x,y解出来了
这里给个式子
我们就把外心坐标求出来了
具体做法
我们考虑暴力来解决这个问题
我们考虑假如我们已经有了i个点的最小圆覆盖
加入第i+1个点有两种情况
在圆内自然不要管
不在圆内,则该点一定在这i+1个点组成的最小圆覆盖上
因为前i个的最小圆覆盖并不包括第i+1个点
想要最小,圆一定过第i+1个点
接下来我们去枚举1~i-1的点j看点j是否能在目前构造出的1~i的最小圆覆盖内
不在,那么同上,点j一定在最小圆覆盖上
i与j构成一个新圆
接着枚举1~j-1的点k看点k是否在目前的1~i的最小圆覆盖内
不在,那么点k一定在最小圆覆盖上
三点确定一个圆,i,j,k的外接圆就是新的最小圆覆盖
为什么是正确的,由于三个点确定一个圆
而目前我们i,j,k都确定在目前1~i的最小圆覆盖上
那么这个圆就是最小圆覆盖
当k继续变大成l再一次不在这个圆内时
此时i,j,l三点一定在最小圆覆盖中,且小于l的点一定也在圆中
j的变大同理
时间复杂度证明
迷惑了!
那么第三重循环执行次数的期望应该是
这是一个常数
那么第二重循环的总复杂度即为
对于进入第一重循环的if的概率同理,是
那么进入if后的复杂度是
故总期望复杂度为
注意上面的分析均为期望复杂度,故我们需要把点的顺序自己打乱,以免被卡
random_shuffle一下即可
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const double eps=1e-12;
struct point{
double x,y;
}a[maxn];
int read(){
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*y;
}
double dis(point p,point q){
double x=p.x-q.x,y=p.y-q.y;
return sqrt(x*x+y*y);
}
point circle_center(point a,point b,point c){
double x1=a.x,x2=b.x,x3=c.x;
double y1=a.y,y2=b.y,y3=c.y;
double A=x1*x1+y1*y1,B=x2*x2+y2*y2,C=x3*x3+y3*y3;
double u1=x1-x2,u2=x1-x3,u3=x2-x3;
double v1=y1-y2,v2=y1-y3,v3=y2-y3;
point o;
o.y=((C-A)*u1-(B-A)*u2)/(2*v1*u2-2*v2*u1);
o.x=((C-A)*v1-(B-A)*v2)/(2*u1*v2-2*u2*v1);
return o;
}
int main(){
int n;
n=read();
for(int i=1;i<=n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y);
random_shuffle(a+1,a+n+1);
point o=a[1];
double r=0;
for(int i=2;i<=n;i++){
if(dis(o,a[i])-r>eps){
o=a[i];r=0;
for(int j=1;j<i;j++){
if(dis(o,a[j])-r>eps){
o.x=(a[i].x+a[j].x)/2;
o.y=(a[i].y+a[j].y)/2;
r=dis(o,a[j]);
for(int k=1;k<j;k++){
if(dis(o,a[k])-r>eps){
o=circle_center(a[i],a[j],a[k]);
r=dis(o,a[k]);
}
}
}
}
}
}
printf("%.10lf\n%.10lf %.10lf",r,o.x,o.y);
return 0;
}