BZOJ2832[AHOI2012] 信号塔

bzoj链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2823
洛谷链接:https://www.luogu.org/problemnew/show/P2533

信号塔

Description

在野外训练中,为了确保每位参加集训的成员安全,实时的掌握和收集周边环境和队员信息非常重要,集训队采用
的方式是在训练所在地散布N个小型传感器来收集并传递信息,这些传感器只与设在集训地中的信号塔进行通信,
信号塔接收信号的覆盖范围是圆形,可以接收到所有分布在该集训区域内所有N个小型传感器(包括在该圆形的边
上)发出的信号。信号塔的功率与信号塔接收范围半径的大小成正比,因为是野外训练,只能使用事先储备好的蓄
电设备,因此在可以收集所有传感器信息的基础上,还应使得信号塔的功率最小。小龙帮助教官确定了一种信号塔
设置的方案,既可以收集到所有N个传感器的信号,又可以保证这个信号塔的功率是最小的。同学们,你们知道,
这个信号塔的信号收集半径有多大,它应该设置在何处吗?

Input

共N+1行,第一行为正整数N(1≤N≤1000000),表示队员个数。接下来N行,每行两个实数用空格分开,分别是第
i个队员的坐标X

Output

一行,共三个实数(中间用空格隔开),分别是信号塔的坐标,和信号塔 覆盖的半径。 (注:队员是否在边界上
的判断应符合他到圆心的距离与信号塔接收半径之差的绝对值小于10^-6

Sample Input

5
1.200 1.200
2.400 2.400
3.800 4.500
2.500 3.100
3.900 1.300

Sample Output

2.50 2.85 2.10

HINT

1≤N≤500000

题解

随机增量法求最小圆覆盖裸题。  
因为是个乱搞的随机玄学算法,就水着讲。。。
随机增量法大概就是在确定最小圆的时候,有三层循环(绝望),遵循以下规则:  
第一层循环的点若不在当前圆内,将其设为圆心(貌似把圆心设为它和第一个点的中点也行),半径不管(玄学)。  
第二层循环的点若不在当前圆內,将圆心设为它和第一层点的中点,直径为他们俩距离。
第三层循环的点若不在当前圆內,则用三层循环中的三个点确定一个圆。  
感觉就是个O(n^3)的暴力,然而由于科学剪枝,期望是O(n)!
具体证明博主不会认为没什么必要讲,因为这种乱搞算法只能做最小圆覆盖,考法很单一,大家记结论就好。
然而,整道题最难搞的部分居然是用三点求圆心。。。
公式推导就是用三个点到圆心的距离等于半径列三个方程,狂解一波就好了。

代码
#include<bits/stdc++.h>
#define db double
using namespace std;
const int M=1e6+5;
const db eps=1e-6;
struct pt{db x,y;};
pt operator + (pt a,pt b){return (pt){a.x+b.x,a.y+b.y};}
pt operator - (pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
pt operator / (pt a,db b){return (pt){a.x/b,a.y/b};}
pt operator * (pt a,db b){return (pt){a.x*b,a.y*b};}
db operator * (pt a,pt b){return a.x*b.y-a.y*b.x;}
pt per[M];
int n;
pt o;db r;
int sig(db x){return (x>eps)-(x<-eps);}
db sqr(db x){return x*x;}
db dis(pt a,pt b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
bool check(pt x){return sig(dis(x,o)-r)<=0;}
pt get(pt a,pt b){return (a+b)/2;}
pt get(pt a,pt b,pt c)
{
    double x,y;
    double ax=sqr(a.x),ay=sqr(a.y),bx=sqr(b.x),by=sqr(b.y),cx=sqr(c.x),cy=sqr(c.y);
    double l1=ax-bx+ay-by,l2=ax-cx+ay-cy;
    double r1=2*a.x-2*b.x,r2=2*a.y-2*b.y,r3=2*a.x-2*c.x,r4=2*a.y-2*c.y;
    x=(l1-r2/r4*l2)/(r1-r2/r4*r3);
    y=(l1-r1/r3*l2)/(r2-r1/r3*r4);
    if(r4==0)x=(a.x+c.x)/2;
    if(r3==0)y=(a.y+c.y)/2;
    return (pt){x,y};
}
void in()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%lf%lf",&per[i].x,&per[i].y);
    random_shuffle(per+1,per+1+n);
}
void ac()
{
    o=per[1];
    for(int i=1;i<=n;++i)
    {
        if(check(per[i])) continue;
        o=per[i];
        for(int j=1;j<i;++j)
        {
            if(check(per[j])) continue;
            o=get(per[i],per[j]);r=dis(o,per[i]);
            for(int k=1;k<j;++k)
            {
                if(check(per[k])) continue;
                o=get(per[i],per[j],per[k]);r=dis(o,per[i]);
            }
        }
    }
    printf("%.2lf %.2lf %.2lf",o.x,o.y,r);
}
int main()
{
    in();ac();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值