BZOJ 1336 [Balkan2002]Alien最小圆覆盖

Problem

BZOJ
BZOJ双倍经验

Solution

其实就是个板子题……用是随机增量法,期望时间复杂度 O(n) O ( n )

每次的操作就是维护一个覆盖前i个点的最小圆。
第i个点不在当前圆内,就以这个点作为圆心,重新扫前面的点
如果j不在圆内,就以这两个点的线段为直径作圆,然后再判断之前的点是否在圆内
如果k不在圆内,就构造过三点的圆,嗯……方法很暴力,直接解析几何。由于三点确定一个圆,所以枚举到第三层就够了。

过三点的圆还有个叉积的奇妙做法,可以戳一下这位dalao的讲解
注意要random_shuffle一下使得数据随机,不然从过程中就可以看出最坏复杂度可能达到 O(n3) O ( n 3 ) 。还有特判掉没有斜率的中垂线和平行的中垂线,感觉可能会有什么情况没有考虑到,但是我也不知道,比较难卡吧,毕竟有random_shuffle。

Code

#include <algorithm>
#include <cstdio>
#include <cmath>
#define rg register
using namespace std;
typedef long long ll;
const int maxn=100010;
const double eps=1e-8;
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct vec{
    double x,y;
    double dis(const vec &t){return sqrt((x-t.x)*(x-t.x)+(y-t.y)*(y-t.y));}
}o,a[maxn];
struct line{
    double k,b;
}l1,l2;
int n;
double r=0;
double fabs(double x){return x>0?x:-x;}
int incircle(vec t)
{
    return t.dis(o)<=r+eps;
}
vec intersection(line a,line b)
{
    double x=(b.b-a.b)/(a.k-b.k);
    double y=a.k*x+a.b;
    return (vec){x,y};
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    read(n);
    for(rg int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
    random_shuffle(a+1,a+n+1);
    for(rg int i=1;i<=n;i++)
      if(!incircle(a[i]))
      {
        o=a[i];r=0;
        for(rg int j=1;j<i;j++)
          if(!incircle(a[j]))
          {
            o.x=(a[i].x+a[j].x)/2;
            o.y=(a[i].y+a[j].y)/2;
            r=o.dis(a[i]);
            for(rg int k=1;k<j;k++)
              if(!incircle(a[k]))
              {
                if(fabs(a[j].y-a[i].y)<=eps||fabs(a[k].y-a[i].y)<=eps) continue;
                l1.k=-(a[j].x-a[i].x)/(a[j].y-a[i].y); 
                l1.b=(a[i].y+a[j].y)/2-l1.k*(a[i].x+a[j].x)/2;
                l2.k=-(a[k].x-a[i].x)/(a[k].y-a[i].y);
                l2.b=(a[i].y+a[k].y)/2-l2.k*(a[i].x+a[k].x)/2;
                if(fabs(l1.k-l2.k)<=eps) continue;
                o=intersection(l1,l2);r=o.dis(a[i]);
              }
          }
      }
    printf("%.10lf\n%.10lf %.10lf\n",r,o.x,o.y);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值