最小圆覆盖

本来不想学的…于是今天就碰到一道大裸题…

例题:bzoj2823 求最小圆覆盖n个点。

伪代码如下:

把所有点随机化,设为(x[1],y[1])...(x[n],y[n])
开始把圆心设为x[1],半径设为0
for i=2 to n
    如果i号点在当前圆内则跳过
    //那么i号点就在圆周上
    把1号点和i号点作为直径作一个圆
    for j=1 to i-1
        如果j号点在当前圆内则跳过
        考虑以j号点和i号点作为直径作一个圆
        for k=1 to j-1
            如果k号点在当前圆内则跳过
            以i,j,k三点组成的三角形的外心作为新圆心
            如果i,j,k三点共线就取ij、ik、jk连线最长的那条作为直径作为新圆

据说期望复杂度是O(n)的。

细节还是比较多的。

首先共线叉积是比较容易判的。如果叉积为0那么就共线。

那么三角形的外心怎么求呢?

如果有3个点,A(x1,y1),B(x2,y2),C(x3,y3)。

AB解析式:(y2-y1)x+(x1-x2)y+x2*y1-x1*y2=0

AB中点:((x1+x2)/2,(y1+y2)/2)

那么AB中垂线解析式就是(y1-y2)x+(x1-x2)y+一些常数,把AB中点的坐标带进去减一下就行了。

然后我们求一下AB和AC中垂线的交点。

两条直线

ax+by=c dx+ey=f

aex+bey=ce dbx+eby=fb

x=(fb-ce)/(ae-db)

同理

adx+bdy=cd adx+aey=af

y=(af-cd)/(ae-bd)

当然如果ae=bd就共线。

所以就解决啦。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <vector>
#include <limits>
#include <set>
#include <map>
using namespace std;
int n;
#define SZ 2333333
typedef double db;
struct pnt
{
    db x,y;
    pnt() {}
    pnt(db a,db b) {x=a; y=b;}
}ps[SZ];
pnt operator - (pnt a,pnt b) {return pnt(a.x-b.x,a.y-b.y);}
db operator * (pnt a,pnt b) {return a.x*b.y-a.y*b.x;}
db pf(db x) {return x*x;}
db dis(pnt a,pnt b) {return sqrt(pf(a.x-b.x)+pf(a.y-b.y));}
pnt mid(pnt a,pnt b) {return pnt((a.x+b.x)/2,(a.y+b.y)/2);}
struct lne
{
    db a,b,c;
    lne() {a=b=c=0;}
    lne(db x,db y,db z) {a=x;b=y;c=z;}
};
pnt operator * (lne a,lne b)
{
    return pnt((b.c*a.b-a.c*b.b)/(a.a*b.b-a.b*b.a),
                (a.c*b.a-b.c*a.a)/(a.a*b.b-a.b*b.a));
}
lne zcx(pnt a,pnt b)
{
    db mx=(a.x+b.x)/2,my=(a.y+b.y)/2;
    db la=a.x-b.x,lb=a.y-b.y,lc=-(mx*la+my*lb);
    return lne(la,lb,lc);
}
pnt wx(pnt a,pnt b,pnt c) {return zcx(a,b)*zcx(a,c);}
db eps=1e-6;
pnt np(pnt a,pnt b,pnt c)
{
    if(fabs((b-a)*(c-a))<eps)
    {
        double abl=dis(a,b),acl=dis(a,c),bcl=dis(b,c),ans=-1;
        pnt ap;
        if(abl>ans) ans=abl, ap=mid(a,b);
        if(acl>ans) ans=acl, ap=mid(a,c);
        if(bcl>ans) ans=bcl, ap=mid(b,c);
        return ap;
    }
    return wx(a,b,c);
}
db gr(pnt o,pnt a,pnt b,pnt c) {return max(max(dis(o,a),dis(o,b)),dis(o,c));}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lf%lf",&ps[i].x,&ps[i].y);
    random_shuffle(ps+1,ps+1+n);
    pnt yx=ps[1]; double r=0;
    for(int i=2;i<=n;i++)
    {
        if(dis(yx,ps[i])<r+eps) continue;
        yx=mid(ps[1],ps[i]);
        r=dis(yx,ps[i]);
        for(int j=1;j<i;j++)
        {
            if(dis(yx,ps[j])<r+eps) continue;
            yx=mid(ps[i],ps[j]);
            r=dis(yx,ps[i]);
            for(int k=1;k<j;k++)
            {
                if(dis(yx,ps[k])<r+eps) continue;
                yx=np(ps[i],ps[j],ps[k]);
                r=gr(yx,ps[i],ps[j],ps[k]);
            }
        }
    }
    printf("%.2lf %.2lf %.2lf\n",yx.x,yx.y,r);
}
时间: 2024-11-10 16:16:18

最小圆覆盖的相关文章

【BZOJ-1336&amp;1337】Alie最小圆覆盖 最小圆覆盖(随机增量法)

1336: [Balkan2002]Alien最小圆覆盖 Time Limit: 1 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 1573  Solved: 697[Submit][Status][Discuss] Description 给出N个点,让你画一个最小的包含所有点的圆. Input 先给出点的个数N,2<=N<=100000,再给出坐标Xi,Yi.(-10000.0<=xi,yi<=10000.0) Outpu

BZOJ1337: 最小圆覆盖

题目:求n个点的最小圆覆盖. 题解:最小圆覆盖,上模板.复杂度证明可以戳:这里 代码: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<iostream> 7 #include<vector> 8 #include<map> 9 #inclu

最小圆覆盖模板

//最小圆覆盖 //输入: 从下标0开始的点集_ps和大小_n //输出: 覆盖所有点的最小圆 //复杂度: O(n) //注意: 会对_ps进行随机处理操作,将会改变点集的内部顺序 Circle MinCoverCir(Point _ps[],int _n) { //随机处理,但是会改变传入的点集. random_shuffle(_ps, _ps+_n);//复杂度O(_n) Circle rec; rec.r = 0; rec.c = _ps[0]; for(int i=1;i<_n;i++

ZOJ 1450 Minimal Circle 最小圆覆盖

套了个模板直接上,貌似没有随机化序列 QAQ //#pragma comment(linker, "/STACK:16777216") //for c++ Compiler #include <stdio.h> #include <iostream> #include <cstring> #include <cmath> #include <stack> #include <queue> #include <

最小圆覆盖 hdu 3007

今天学习了一下最小圆覆盖, 看了一下午都没看懂, 晚上慢慢的摸索这代码,接合着别人的讲解, 画着图跟着代码一步一步的走着,竟然有些理解了. 最小圆覆盖: 给定n个点, 求出半径最小的圆可以把这些点全部包围, 可以在圆的边界上 下面是我的个人理解. 如果不对, 还请路过大牛指出 先找一个点, 让圆心等于这个点的坐标, 半径等于0, 显然这个对的, 接着找下一个点, 如果只有两个点的话, 那么最小的圆一定是以他们为直径做圆, 接着找第三个点, 如果第三个点在园内或者在边界上, 那么不用更新当前的最小

BZOJ 2823 AHOI 2012 信号塔 凸包+最小圆覆盖

题目大意:给出平面上n个点,求最小圆覆盖. 思路:圆覆盖问题只与所有点中凸包上的点有关,因此先求一下凸包,然后数据范围骤减.大概是只剩下logn左右个点.这样就可以随便浪了. 先找所有三个点组成的圆,然后找两个点为直径所组成的圆. 还有就是三角形的外心公式,简直不是人推的,然后我就机制的百度了,结果如下: 不要模拟退火... 样例很坑,当你算出2.49 2.86的时候,不要认为你被卡精了,其实是你写挂了... CODE: #include <cmath> #include <cstdio

【bzoj1336/1337/2823】[Balkan2002]Alien最小圆覆盖 随机增量法

题目描述 给出N个点,让你画一个最小的包含所有点的圆. 输入 先给出点的个数N,2<=N<=100000,再给出坐标Xi,Yi.(-10000.0<=xi,yi<=10000.0) 输出 输出圆的半径,及圆心的坐标 样例输入 6 8.0 9.0 4.0 7.5 1.0 2.0 5.1 8.7 9.0 2.0 4.5 1.0 样例输出 5.00 5.00 5.00 题解 随机增量法求最小圆覆盖裸题 求法:设初始圆为某空圆,先枚举第一个点,如果不在当前圆内,则令当前圆为这一个点的最小圆

HDOJ 2215 Maple trees 最小圆覆盖

增量法最小圆覆盖.... Maple trees Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1646    Accepted Submission(s): 510 Problem Description There are a lot of trees in HDU. Kiki want to surround all the t

【bzoj2280】[Poi2011]Plot 二分+倍增+二分+最小圆覆盖

题目描述 给出一系列点p_1, p_2, ... , p_n,将其分成不多余m个连续的段,第i段内求一个点q_i,使得q_i到这段内点的距离的最大值的最大值最小 输入 第一行,n m下面n行,每行两个整数,表示p_i的x y坐标1<=m<=n<=100000坐标范围[-1000000,1000000] 0<p,q,r<=150,输入中至少包含一个’N’ 输出 第一行,q_i到这段内点的距离的最大值的最大值的最小值第二行,分成的段数k下面k行,每行两个实数,表示q_k的x y坐