洛谷 P2533 [AHOI2012]信号塔 (最小圆覆盖)

48 篇文章 0 订阅
12 篇文章 0 订阅

题面

题意

给出n个点,输出最小覆盖圆的坐标和半径

做法

首先用random_shuffle随机化点的顺序,然后做以下操作:
1.枚举所有点(设为i从2到n),若有点在当前圆外(一开始的圆为第一个点,半径为0),则将圆心变为这个点,半径改为0,进入2.
2.枚举已经考虑过的点(设为j从1到i-1),若有点在当前圆外,将圆心变为i,j两点的中点,半径为两点距离的一半.
3.枚举j之前的的点(设为k从1到j-1),若有点在圆外,则更新为i,j,k三点的外接圆,同时更新半径.

复杂度:目测是O(n^3)的,但因为点是随机的(这很重要),所以从概率的角度上看是O(n)的.

代码(解析几何)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<algorithm>
#define db double
#define N 1000100
#define eps (1e-8)
using namespace std;

int n;
db r;
struct Node
{
    db x,y;
} node[N],mid;

struct Xn
{
    db k,b;
};

//计算两点之间的距离
inline db len(Node u,Node v)
{
    return sqrt((u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y));
}

//判断点是否在圆外
inline bool out(Node u)
{
    return(len(u,mid)>r+eps);
}

//求直线的解析式
inline Xn jx(Node u,Node v)
{
    Xn res;
    res.k=(u.y-v.y)/(u.x-v.x),res.b=u.y-res.k*u.x;
    return res;
}

//算两点的中点
inline Node middle(Node u,Node v)
{
    Node res;
    res.x=(u.x+v.x)/2,res.y=(u.y+v.y)/2;
    return res;
}

//求与该直线垂直的直线
inline Xn cz(Xn u,Node v)
{
    Xn res;
    res.k=-1/(u.k),res.b=v.y-v.x*res.k;
    return res;
}

//求该横坐标在直线上对应的点
inline Node qd(Xn u,db v)
{
    Node res;
    res.x=v,res.y=u.k*v+u.b;
    return res;
}

//求两直线的交点
inline Node jd(Xn u,Xn v)
{
    db res;
    res=(v.b-u.b)/(u.k-v.k);
    return qd(u,res);
}

//求三个点的外接圆
inline Node wj(Node u,Node v,Node w)
{
    Xn a,b;
    a=jx(u,v),b=jx(v,w);
    if(qd(a,w.x).y==w.y) //三点一线的情况下
    {
        Node res;
        res.x=(max(u.x,max(v.x,w.x))+min(u.x,min(u.x,v.x)))/2,res.y=(min(u.x,min(v.y,w.y))+min(u.y,min(u.y,v.y)))/2;
        return res;
    }
    a=cz(a,middle(u,v)),b=cz(b,middle(v,w));
    return jd(a,b);
}

int main()
{
    srand(time(0));
    register int i,j,k;
    cin>>n;
    for(i=1; i<=n; ++i)
    {
        scanf("%lf%lf",&node[i].x,&node[i].y);
    }
    random_shuffle(node+1,node+n+1),mid=node[1],r=0;
    for(i=2; i<=n; ++i)
    {
        if(out(node[i]))
        {
            mid=node[i],r=0;
            for(j=1; j<i; ++j)
            {
                if(out(node[j]))
                {
                    mid=middle(node[i],node[j]),r=len(mid,node[i]);
                    for(k=1; k<j; ++k)
                    {
                        if (out(node[k]))
                            mid=wj(node[i],node[j],node[k]),r=len(mid,node[i]);
                    }
                }
            }
        }
    }
    printf("%.2lf %.2lf %.2lf",mid.x,mid.y,r);
}

代码(计算几何)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define db double
#define eps 1e-8
#define N 1001000
using namespace std;

int n;
db r;
struct Node
{
    db x,y;
    void make(db u,db v){x=u,y=v;}
    Node operator + (const Node &u) const{Node res;res.x=x+u.x,res.y=y+u.y;return res;}
    Node operator - (const Node &u) const{Node res;res.x=x-u.x,res.y=y-u.y;return res;}
    Node operator * (const db &u) const{Node res;res.x=u*x,res.y=y*u;return res;}
    Node operator / (const db &u) const{Node res;res.x=x/u,res.y=y/u;return res;}
    bool operator < (const Node &u){return x<u.x;}
    bool operator > (const Node &u){return x>u.x;}
} node[N],ans;
struct Xn
{
    Node a,v;
};

inline db lenf(Node u,Node v){return (u.x-v.x)*(u.x-v.x)+(u.y-v.y)*(u.y-v.y);}
inline db len(Node u,Node v){return sqrt(lenf(u,v));}
inline bool out(Node u){return lenf(u,ans)>r*r+eps;}
inline Node mid(Node u,Node v){return (u+v)/2.0;}
inline db cj(Node u,Node v){return u.x*v.y-u.y*v.x;}
inline Node xz(Node u){Node res;res.make(-u.y,u.x);return res;}
inline Node jd(Xn u,Xn v)
{
    db t=cj(u.v,(v.a-u.a))/cj(v.v,u.v);
    return v.a+v.v*t;
}

inline Node wj(Node u,Node v,Node w)
{
    Xn a,b;
    a.a=mid(u,v),b.a=mid(u,w);
    a.v=xz(v-u),b.v=xz(w-u);
    if(fabs(cj(a.v,b.v))<eps)
    {
        if(u>v) swap(u,v);
        if(u>w) swap(u,w);
        if(v>w) swap(v,w);
        return mid(u,w);
    }
    return jd(a,b);
}

int main()
{
    srand(517);
    int i,j,k;
    Node a,b,c;
    /*
    a.make(0,0),b.make(0,3),c.make(-3,-3);
    a=wj(a,b,c);
    cout<<a.x<<" "<<a.y;
    //*/
    cin>>n;
    for(i=1; i<=n; i++)
    {
        scanf("%lf%lf",&node[i].x,&node[i].y);
    }
    random_shuffle(node+1,node+n+1);
    ans=node[1],r=0;
    for(i=2; i<=n; i++)
    {
        if(!out(node[i])) continue;
        ans=node[i];
        r=0;
        for(j=1; j<i; j++)
        {
            if(!out(node[j])) continue;
            ans=mid(node[i],node[j]);
            r=len(node[i],ans);
            for(k=1; k<j; k++)
            {
                if(!out(node[k])) continue;
                ans=wj(node[i],node[j],node[k]);
                r=len(node[i],ans);
            }
        }
    }
    printf("%.2f %.2f %.2f",ans.x,ans.y,r);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值