2179:2、信号塔

方法一:随机增量法:
分析:用最小的圆覆盖住所有的点:随机增量法复杂度是O(n);

2、算法及原理算法介绍:我们本次算法的设计是基于这样一个简单直观的性质:
在既定的给定点条件下,如果引入一张新的半平面,只要此前的最优解顶点(即唯一确定最小包围圆的几个关键顶点)能够包含于其中,则不必对此最优解进行修改,亦即此亦为新点集的最优解;否则,新的最优解顶点必然位于这个新的半空间的边界上。定理可以通过反证法证明。于是,基于此性质,我们便可得到一个类似于线性规划算法的随机增量式算法。定义Di为相对于pi的最小包围圆。此算法实现的关键在于对于pi∉Di-1时的处理。显然,如果pi∈Di-1,则Di= Di-1;否则,需要对Di另外更新。而且,Di的组成必然包含了pi;因此,此种情况下的最小包围圆是过pi点且覆盖点集{ p1 ,p2 ,p3 ……pi-1}的最小包围圆。则仿照上述处理的思路,Di={ p1 ,pi },逐个判断点集{ p2 ,p3 ……pi-1 },如果存在pj∉ Di,则Di={pj,pi }。同时,再依次对点集{ p1 ,p2 ,p3 ……pj-1 }判断是否满足pk∈Di,若有不满足,则Di={pk ,pj,pi }。由于,三点唯一地确定一个圆,故而,只需在此基础上判断其他的点是否位于此包围圆内,不停地更新pk。当最内层循环完成时,退出循环,转而更新pj;当次内层循环结束时,退出循环,更新pi。当i=n时,表明对所有的顶点均已处理过 ,此时的Dn即表示覆盖了给定n个点的最小包围圆。

总结:
假设圆O是前i-1个点得最小覆盖圆,加入第i个点,如果在圆内或边上则什么也不做。否,新得到的最小覆盖圆肯定经过第i个点。然后以第i个点为基础(半径为0),重复以上过程依次加入第j个点,若第j个点在圆外,则最小覆盖圆必经过第j个点。重复以上步骤(因为最多需要三个点来确定这个最小覆盖圆,所以重复三次)。遍历完所有点之后,所得到的圆就是覆盖所有点得最小圆。证明可以考虑这么做:最小圆必定是可以通过不断放大半径,直到所有以任意点为圆心,半径为半径的圆存在交点,此时的半径就是最小圆。所以上述定理可以通过这个思想得到。这个做法复杂度是O(n)的,当加入圆的顺序随机时,因为三点定一圆,所以不在圆内概率是3/i,求出期望可得是O(n)。

方法二:模拟退火法:对于每个枚举的点找到改点到所给点的最远点的距离,然后保证这个距离最小,即为所求圆的半径;
以下代码采用的是方法一

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
const double eps = 1e-8;
struct Point {
    double x, y;
} a[N], O;
double R;
double sqr(double x) {return x * x;}
double dis(Point a, Point b) {return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));}
void work(Point p1, Point p2, Point p3) {
    double a, b, c, d, e, f;
    a = p2.y - p1.y; b = p3.y - p1.y; c = p2.x - p1.x; d = p3.x - p1.x;
    f = p3.x * p3.x + p3.y * p3.y - p1.x * p1.x - p1.y * p1.y;
    e = p2.x * p2.x + p2.y * p2.y - p1.x * p1.x - p1.y * p1.y;
    O.x = (a * f - b * e) / (2 * a * d - 2 * b * c); O.y = (d * e - c * f) / (2 * a * d - 2 * b * c);
    R = dis(O, p1);
}
int main() {
    int n;
    scanf("%d", &n);
    for (int i=1; i<=n; ++i) scanf("%lf%lf", &a[i].x, &a[i].y);
    random_shuffle(a+1, a+n+1);
    O=a[1]; R=0;
    for (int i=2; i<=n; ++i)
        if (dis(a[i], O) > R + eps) {
            O = a[i]; R = 0;
            for (int j=1; j<i; ++j)
                if (dis(a[j], O) > R + eps) {
                    O.x = (a[i].x + a[j].x) / 2; O.y = (a[i].y + a[j].y) / 2; R = dis(a[j], O);
                    for (int k=1; k<j; ++k) if (dis(a[k], O) > R + eps) work(a[i], a[j], a[k]);
                }
        }
    printf("%.2lf %.2lf %.2lf\n", O.x, O.y, R);
    return 0;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值