随机增量法求解最小圆覆盖问题

过程

假设我们已经通过某种神秘的方法确定了前 \(i - 1\) 个点的圆 \(O\),这这时候我们考虑加入第 \(i\) 个点,此时,如果第 \(i\) 个点在圆 \(O\) 内(包括边界上),我们就可以认为前 \(i\) 个点的圆也是 \(O\),否则我们需要重新确定前 \(i\) 个点的最小圆覆盖。具体的方法就是……暴力:

  1. 首先第 \(i\) 个点一定在前 \(i\) 个点的圆的圆周上。所以我们初始化一个以第 \(i\) 个点为圆心,半径为 0 的圆。
  2. 有了初始化的圆,我们要根据前面的点去调整这个圆,如果不在圆内,那就调整圆心为两点的中点,半径为两点间距离的一半。
  3. 有了初步更新的圆,我们再根据前面的点去调整,如果不在圆内,就根据三个点调整圆心和半径。

根据以上步骤,我们得出了以下循环:

void solve() {
    // code ...

    // 将读入的点集重新排列
    // 防止被特定的数据卡
    std::random_shuffle(a, a + n);
    
    // 对于一个点,最小圆就是半径为 0 的。
    point O = a[0];
    double r = 0;
    // 逐步加入第 i 个点
    for (int i = 1; i < n; i++) {
        // 在圆就不变
        if (dis2(O, a[i]) - r <= EPS) {
            continue;
        }

        // 不在就重新确定圆
        O = a[i], r = 0;
        for (int j = 0; j < i; j++) {
            if (dis2(O, a[j]) - r <= EPS) {
                continue;
            }
            // 由两个点确定的最小圆
            O = cen(a[i], a[j]), r = dis2(O, a[j]);
            for (int k = 0; k < j; k++) {
                if (dis2(O, a[k]) - r <= EPS) {
                    continue;
                }
                // 由三个点确定的最小圆
                O = cen(a[i], a[j], a[k]), r = dis2(O, a[i]);
            }
        }
    }

    // code ...
}

最后跑完就得到了所有点的最小圆,以上三个循环可以跑 \(10^5\) 的数据,据说时间复杂度还是 \(O(n)\) 的。

posted @ 2025-03-15 13:23  Young_Cloud  阅读(42)  评论(0)    收藏  举报