【AHOI2012】信号塔

题面

题解

xgzc怒切计算几何

最小圆覆盖板子题

整体算法如下:

枚举第一个点,考虑当前圆是否包含了这个点,如果没有,则把圆变成以这个点为圆心,半径为\(0\)的圆。再枚举第二个点,考虑圆是否包含了这个点,如果没有,则把圆变成以这两个点的中点为圆心,半径为两点距离一半的圆。再枚举第三个点,节点是否在圆内,如果不在,则把圆直接变成这三个点的外接圆。

\(n^3\)过百万???是的

我们随机打乱点,这样的期望是\(O(n)\)

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
    int data = 0, w = 1; char ch = getchar();
    while(ch != '-' && (!isdigit(ch))) ch = getchar();
    if(ch == '-') w = -1, ch = getchar();
    while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    return data * w;
}

const int maxn(1e6 + 10);
const double eps(1e-10), pi(acos(-1));
struct point { double x, y; };
struct line { point a, v; };
struct circle { point o; double r; } O;
typedef point vector;
inline vector operator + (const vector &lhs, const vector &rhs)
    { return (vector) {lhs.x + rhs.x, lhs.y + rhs.y}; }
inline vector operator - (const vector &lhs, const vector &rhs)
    { return (vector) {lhs.x - rhs.x, lhs.y - rhs.y}; }
inline vector operator * (const vector &lhs, const double &rhs)
    { return (vector) {lhs.x * rhs, lhs.y * rhs}; }
inline vector operator / (const vector &lhs, const double &rhs)
    { return (vector) {lhs.x / rhs, lhs.y / rhs}; }
inline double operator * (const vector &lhs, const vector &rhs)
    { return lhs.x * rhs.x + lhs.y * rhs.y; }
inline double cross(const vector &lhs, const vector &rhs)
    { return lhs.x * rhs.y - lhs.y * rhs.x; }
inline double Len(const vector &a) { return sqrt(a.x * a.x + a.y * a.y); }
inline double Dis(const point &a, const point &b) { return Len(a - b); }
inline vector rotate(const vector &lhs, const double &ang)
{
    double c = cos(ang), s = sin(ang);
    return (vector) {lhs.x * c - lhs.y * s, lhs.x * s + lhs.y * c};
}

point Intersection(const line &a, const line &b)
{
    vector c = b.a - a.a;
    double t = cross(b.v, c) / cross(b.v, a.v);
    return a.a + a.v * t;
}

line half(const line &a)
{
    point b = a.a + a.v * .5;
    return (line) {b, rotate(a.v, pi * .5)};
}

void getCircle(point p[], int n)
{
    std::random_shuffle(p + 1, p + n + 1);
    for(RG int i = 1; i <= n; ++i)
        if(Dis(O.o, p[i]) > O.r)
        {
            O.o = p[i], O.r = 0;
            for(RG int j = 1; j < i; j++)
                if(Dis(O.o, p[j]) > O.r)
                {
                    O.o = (p[i] + p[j]) * .5, O.r = Dis(p[i], p[j]) * .5;
                    for(RG int k = 1; k < j; k++)
                        if(Dis(O.o, p[k]) > O.r)
                            O.o = Intersection(half((line) {p[i], p[j] - p[i]}),
                                    half((line) {p[i], p[k] - p[i]})),
                            O.r = Dis(O.o, p[i]);
                }
        }
    printf("%.2lf %.2lf %.2lf\n", O.o.x, O.o.y, O.r);
}

int n; point p[maxn];
int main()
{
    srand(time(NULL)); scanf("%d", &n);
    for(RG int i = 1; i <= n; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
    getCircle(p, n);
    return 0;
}

转载于:https://www.cnblogs.com/cj-xxz/p/10336956.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值