过程
假设我们已经通过某种神秘的方法确定了前 \(i - 1\) 个点的圆 \(O\),这这时候我们考虑加入第 \(i\) 个点,此时,如果第 \(i\) 个点在圆 \(O\) 内(包括边界上),我们就可以认为前 \(i\) 个点的圆也是 \(O\),否则我们需要重新确定前 \(i\) 个点的最小圆覆盖。具体的方法就是……暴力:
- 首先第 \(i\) 个点一定在前 \(i\) 个点的圆的圆周上。所以我们初始化一个以第 \(i\) 个点为圆心,半径为 0 的圆。
- 有了初始化的圆,我们要根据前面的点去调整这个圆,如果不在圆内,那就调整圆心为两点的中点,半径为两点间距离的一半。
- 有了初步更新的圆,我们再根据前面的点去调整,如果不在圆内,就根据三个点调整圆心和半径。
根据以上步骤,我们得出了以下循环:
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)\) 的。