正题
最小圆覆盖是一个模板。
在n个点中,构造一个尽量小的圆,使其覆盖所有的点。
很明显,如果边上有一个点或者两个点,那么圆的大小可以再缩小。边上三个点的时候就刚刚好可以固定一个圆。
那么我们很容易就可以打出一个n的三次方的模板,只要经过一定的优化就可以打出下面的模板。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n;
struct node{
double x,y;
}s[100010],ci;
double r=0;
double dou(double x){
return x*x;
}
double get_dis(node x,node y){
return sqrt(dou(x.x-y.x)+dou(x.y-y.y));
}
int calc_c(double a,double b,double c,double d,double e,double f){
ci.x=(e*d-b*f)/(a*d-b*c);
ci.y=(e*c-a*f)/(b*c-a*d);
return 1;
}
bool uninside(node x){
return get_dis(ci,x)>r;
}
void get_ans(){
ci.x=0,ci.y=0;r=0;
for(int i=1;i<=n;i++){
if(!uninside(s[i])) continue;
ci=s[i];
r=0;
for(int j=1;j<i;j++)
if(uninside(s[j])){
ci.x=(s[i].x+s[j].x)/2;
ci.y=(s[i].y+s[j].y)/2;
r=get_dis(ci,s[i]);
for(int k=1;k<j;k++)
if(uninside(s[k])) calc_c(s[i].x-s[j].x,s[i].y-s[j].y,s[i].x-s[k].x,
s[i].y-s[k].y,(dou(s[i].x)-dou(s[j].x)-dou(s[j].y)+dou(s[i].y))/2,
(dou(s[i].x)-dou(s[k].x)-dou(s[k].y)+dou(s[i].y))/2),r=get_dis(ci,s[i]);
}
}
printf("%.10lf\n%.10lf %.10lf",r,ci.x,ci.y);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf %lf",&s[i].x,&s[i].y);
random_shuffle(s+1,s+1+n);
get_ans();
}
里面那个长长的就是已知三个点,求圆的方程,不会的可以自己推一推,在这里不多说。
看起来好像是n的三次方的复杂度,其实随机上来说,复杂度是接近n的。
因为一个点不在 能覆盖前面的圆 几率只是3/i,因为第i个点在那之外,它只会替换那三个点中的任意一个点。而剩下的几率就是不替换,所以几率是3/i;
那么倒数第二层的进入几率就是3/i,每次复杂度是j,所以一共3n,否则就是1的复杂度。
加起来接近线性。