最小圆覆盖

给出平面上N个点,请求出一个半径最小的圆覆盖住所有的点
洛谷P1742 最小圆覆盖
洛谷P2533 [AHOI2012]信号塔


显然易证最优解的圆一定是以某两个点连线为直径的圆某三个点组成的三角形的外接圆
初始将圆心定为第一个点,R=0
1.枚举第一个点 i,若点 i 不在目前圆内,设它为圆心,进入2
2.再枚举第二个点 j,若点 j 不在当前圆内,设当前圆为以 i,j 为直径的圆,进入3
3.枚举第三个点 k,若点 k 不在当前圆内,设当前圆为 i,j,k 的外接圆

看似 O ( n 3 ) O(n^3) O(n3)的复杂度,但点集随机打乱后期望复杂度 O ( n ) O(n) O(n)蒟蒻不会证

关于三点定圆
设三个点为 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x 3 , y 3 ) (x_1,y_1),(x_2,y_2),(x_3,y_3) (x1,y1),(x2,y2),(x3,y3),圆心为 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)
带入圆的标准方程
{ ( x 1 − x 0 ) 2 + ( y 1 − y 0 ) 2 = r 2 ( x 2 − x 0 ) 2 + ( y 2 − y 0 ) 2 = r 2 ( x 3 − x 0 ) 2 + ( y 3 − y 0 ) 2 = r 2 \left\{\begin{aligned} (x_1-x_0)^2+(y_1-y_0)^2=r^2 \\(x_2-x_0)^2+(y_2-y_0)^2=r^2\\ (x_3-x_0)^2+(y_3-y_0)^2=r^2 \end{aligned}\right. (x1x0)2+(y1y0)2=r2(x2x0)2+(y2y0)2=r2(x3x0)2+(y3y0)2=r2
②-①,③-②之后化简
{ ( x 2 − x 1 ) x 0 + ( y 2 − y 1 ) y 0 = ( x 2 2 − x 1 2 ) − ( y 1 2 − y 2 2 ) 2 ( x 3 − x 2 ) x 0 + ( y 3 − y 2 ) y 0 = ( x 3 2 − x 2 2 ) − ( y 2 2 − y 3 2 ) 2 \left\{\begin{aligned} (x_2-x_1)x_0+(y_2-y_1)y_0=\frac{(x_2^2-x_1^2)-(y_1^2-y_2^2)}{2} \\(x_3-x_2)x_0+(y_3-y_2)y_0=\frac{(x_3^2-x_2^2)-(y_2^2-y_3^2)}{2} \end{aligned}\right. (x2x1)x0+(y2y1)y0=2(x22x12)(y12y22)(x3x2)x0+(y3y2)y0=2(x32x22)(y22y32)
解二元一次方程即可
a = x 2 − x 1 , b = y 2 − y 1 , c = x 3 − x 2 , d = y 3 − y 2 , e = ( x 2 2 − x 1 2 ) − ( y 1 2 − y 2 2 ) , f = ( x 3 2 − x 2 2 ) − ( y 2 2 − y 3 2 ) a=x_2-x_1,b=y_2-y_1,c=x_3-x_2,d=y_3-y_2,e=(x_2^2-x_1^2)-(y_1^2-y_2^2),f=(x_3^2-x_2^2)-(y_2^2-y_3^2) a=x2x1,b=y2y1,c=x3x2,d=y3y2,e=(x22x12)(y12y22),f=(x32x22)(y22y32)
x 0 = ( f ∗ b − e ∗ d ) / ( c ∗ b − a ∗ d ) / 2.0 , y 0 = ( a ∗ f − e ∗ c ) / ( a ∗ d − b ∗ c ) / 2.0 x_0=(f*b-e*d)/(c*b-a*d)/2.0,y_0=(a*f-e*c)/(a*d-b*c)/2.0 x0=(fbed)/(cbad)/2.0,y0=(afec)/(adbc)/2.0

#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long lt;
typedef double dd;
#define eps 1e-6
#define sqr(x) ((x)*(x))

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=1000010;
int n;
struct point{dd x,y;}p[maxn],O;
dd R;

dd getd(point a,point b){ return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
point getO(point p1,point p2,point p3)
{
    point res;
    dd a=p2.x-p1.x,b=p2.y-p1.y,c=p3.x-p2.x,d=p3.y-p2.y;
    dd e=sqr(p2.x)+sqr(p2.y)-sqr(p1.x)-sqr(p1.y);
    dd f=sqr(p3.x)+sqr(p3.y)-sqr(p2.x)-sqr(p2.y);
    res.x=(f*b-e*d)/(c*b-a*d)/2.0; 
    res.y=(a*f-e*c)/(a*d-b*c)/2.0;
    return res;
}

void mincir()
{
    O=p[1]; R=0;
    for(int i=1;i<=n;++i)
    if(getd(p[i],O)-R>eps)
    {
        O=p[i]; R=0;
        for(int j=1;j<i;j++)
        if(getd(p[j],O)-R>eps)
        {
            O=(point){(p[i].x+p[j].x)/2.0,(p[i].y+p[j].y)/2.0};
            R=getd(p[i],p[j])/2.0;
    
            for(int k=1;k<j;++k)
            if(getd(p[k],O)-R>eps)
            {
                O=getO(p[i],p[j],p[k]);
                R=getd(p[i],O);
            }
        }
    }
} 

int main()
{
    n=read();
    for(int i=1;i<=n;++i)
    scanf("%lf%lf",&p[i].x,&p[i].y);
    
    random_shuffle(p+1,p+1+n);
    
    mincir();
    printf("%.2lf %.2lf %.2lf",O.x,O.y,R);
    return 0;
}

你可能感兴趣的:(计算几何)