最小覆盖圆解决的问题模板大概是:
在一个平面内的很多点中,找出一个最小的圆,使之覆盖所有的点。
思路
假设点的个数为 nnn, 最小覆盖圆面积为 RRR。
当 n=1n = 1n=1 时,毋庸置疑,此时 R=0R = 0R=0;
当 n=2n = 2n=2 时,此时R应该为 R=dis(point1,point2)/2R = dis(point1, point2) / 2R=dis(point1,point2)/2
当 n=3n = 3n=3 时,那么 RRR 就是三个点形成的最小圆,也就是三个点形成的三角形的外接圆。
那么 n>3n > 3n>3 时呢?我们可以延续刚刚的思路
因为在取点的时候,可能第一次或者之后取的两个点三个点做出的圆就包含了所有点, 导致所求圆面积不是最小的,所以在计算之前需要把取点的顺序打乱,也就是random一下。
由于只需要三个点就可以确定一个圆了,其圆心就是三角形的外接圆圆心(两个点的话那就两点中点。。),那么我们来模拟一下过程:
① 首先,将所有点随机排列,使用random_shuffle() 函数。
② 设此时的最小圆圆心为O0,半径为R0。在点中取出一点 iii,若i在圆O0内,则遍历下一个点,否则就将其作为当前圆的圆心,R=0R = 0R=0,就是 n=1n = 1n=1的情况,因为我们需要构造新的圆,使之完全包括前i个点,有一个点i了,那就还需要再找两个。
③ 再依次取出i之前的点,作为第二个点j,如果没有一个是在R的外面的话就表示当前点i之前的所有的点都在我们之前计算的最小圆中,再进行下去就没有意义,所以我们需要遍历下一个i点,继续重复我们的计算,也就是i++,然后回到②。每当遇到一个不在R中的点,这时的 R=dis(i,j)/2R = dis(i, j) / 2R=dis(i,j)/2 ,就是 n=2n = 2n=2 的情况,然后就进行下一步。
④ 依次取出j之前的所有点, 作为第三个点k,同样地,如果没有一个点在当前我们计算的最小圆的外面,那么再进行下去也没有意义,就需要遍历下一个点j,也就是 j++j ++j++ ,然后回到③。每当遇到一个不在R中的点,就相当于找到了第三个点,这三个点都刚好不在我们最初计算的圆O0中,而且刚好能做出一个圆刚刚好包含点1~k的圆且,点i、j也都在这个圆的边界上。
那么 k+1 j−1k + 1 ~ j - 1k+1 j−1 之间的点呢?所以我们需要继续重复次步骤,也就是把j之前的所有点都当作一次第三个点,从而计算出能刚刚好包含前 j−1j - 1j−1 个点的圆同理,②、③的回溯也是这个原因,这样一直计算到i == n的话,求出的圆就是能包含n个点的最小圆了。
所以此处只需要三重循环,再加三个判断就能计算了,额这个算法的时间复杂度。其实只有 O(n)O(n)O(n) 啦,算一下。
tips:
求三角形的外接圆,可以用直线方程、也可以运用参数方程,从而求得两条直线的中垂线,其焦点即为圆心。也可以运用向量的思想求得。
实现模板:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const double eds = 1e-12;
struct Node{
double x, y;
}node[maxn];// 点
double R = 0; //半径
double sq(double x) {return x * x;}
double dis(double x1, double y1, double x2, double y2) {return sqrt(sq(x1 - x2) + sq(y1 - y2));}
int main(){
ios::sync_with_stdio(0);
cin.tie(); cout.tie(0);
int n; cin >> n;
for(int i = 0; i < n; i ++) {
cin >> node[i].x >> node[i].y;
}
random_shuffle(node, node + n);//将点随机排列,防止好心人教你wa题
double r = node[0].x, l = node[0].y; //圆心的横、纵坐标
double x1, x2, x3, y1, y2;
for(int i = 1; i < n; i ++){
if(dis(node[i].x, node[i].y, r, l) - R > eds){ //不在半径为R的圆内
x1 = node[i].x, y1 = node[i].y;
r = x1; l = y1; R = 0;
for(int j = 0; j < i; j ++){
if(dis(node[j].x, node[j].y, r, l) - R > eds){ //不在半径为R的圆内
x2 = node[j].x, y2 = node[j].y;
r = (x1 + x2) / 2.0; l = (y1 + y2) / 2.0;
R = dis(r, l, x2, y2);
for(int k = 0; k < j; k ++){
if(dis(node[k].x, node[k].y, r, l) - R > eds){ //不在半径为R的圆内
x3 = node[k].x, y3 = node[k].y;
double A = sq(x1) + sq(y1), B = sq(x2) + sq(y2), C = sq(x3)+ sq(y3);
double u1 = x1 - x2, u2 = x1 - x3, u3 = x2 - x3;
double v1 = y1 - y2, v2 = y1 - y3, v3 = y2 - y3;
l = ((C - A) * u1 - (B - A) * u2) / (2 * v1 * u2 - 2 * v2 * u1);
r = ((C - A) * v1 - (B - A) * v2) / (2 * u1 * v2 - 2 * u2 * v1);
R = dis(x1, y1, r, l);
}
}
}
}
}
}
cout << R * 2.0 << "\n";
return 0;
}