给出N个点,让你画一个最小的包含所有点的圆。
给出N个点,让你画一个最小的包含所有点的圆。
先给出点的个数N,2<=N<=100000,再给出坐标Xi,Yi.(-10000.0<=xi,yi<=10000.0)
输出圆的半径,及圆心的坐标
经典模型,随机增量法求最小圆覆盖。
假如已经求出了前i-1个点的最小圆覆盖,假如第i个点后,如果不在所求的圆內,那么这个点一定在新圆的边界上。这样我们只要枚举另外两个点就可以了,因为三点确定一个圆。
枚举方法为:从1-(i-1)枚举一个不在圆內的点j,再从1-(j-1)枚举一个不在圆內的点k,那么i j k三点就可以确定一个圆。(注意三点共线的特殊情况)
这样的枚举方法一定可以保证枚举到所有的点对,从而保证所有点在圆內且圆最小。(傻傻的我想这个想了好久……)
这样的枚举复杂度看似是O(n^3),但在顺序随机的情况下可以证明复杂度是O(n)。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 100005 #define eps 1e-8 using namespace std; int n; double r; struct P { double x,y; friend P operator +(P a,P b){return (P){a.x+b.x,a.y+b.y};} friend P operator -(P a,P b){return (P){a.x-b.x,a.y-b.y};} friend P operator /(P a,double b){return (P){a.x/b,a.y/b};} }p[maxn],t; inline double dis(P a,P b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } inline P rev(P a) { return (P){a.y,-a.x}; } inline P centre(P a,P b,P c) { if (fabs((a.x-b.x)*(a.y-c.y)-(a.x-c.x)*(a.y-b.y))<=eps) { if (a.x<b.x) swap(a,b); if (a.x<c.x) swap(a,c); if (b.x<c.x) swap(b,c); return (a+c)/2; } P x1=(a+b)/2,x2=x1+rev(a-b),y1=(a+c)/2,y2=y1+rev(a-c); if (fabs(y1.x-y2.x)<eps) swap(x1,y1),swap(x2,y2); double k2=(y1.y-y2.y)/(y1.x-y2.x),b2=y2.y-y2.x*k2; if (fabs(x1.x-x2.x)<eps) return (P){x1.x,k2*x1.x+b2}; double k1=(x1.y-x2.y)/(x1.x-x2.x),b1=x2.y-x2.x*k1; double x=(b2-b1)/(k1-k2); return (P){x,k1*x+b1}; } int main() { scanf("%d",&n); F(i,1,n) scanf("%lf%lf",&p[i].x,&p[i].y); random_shuffle(p+1,p+n+1); t=p[1];r=0.0; F(i,2,n) if (dis(t,p[i])>r+eps) { t=(p[1]+p[i])/2;r=dis(p[i],t); F(j,2,i-1) if (dis(t,p[j])>r+eps) { t=(p[i]+p[j])/2;r=dis(p[i],t); F(k,1,j-1) if (dis(t,p[k])>r+eps) { t=centre(p[i],p[j],p[k]); r=dis(p[i],t); } } } printf("%.10lf\n%.10lf %.10lf\n",r,t.x,t.y); return 0; }