给定平面上的n nn个点,求一个最小的圆使得所有的点都落在圆内。
求解上述问题便是W e l z l WelzlWelzl算法的作用。其期望复杂度可以达到O ( n ) O(n)O(n)。
W e l z l WelzlWelzl算法的正确性基于一个定理:
定理:对于平面上任意n nn个点,在其最小覆盖圆外取第n + 1 n+1n+1个点。那么第n + 1 n+1n+1个点一定在这n + 1 n+1n+1个点的最小覆盖圆上。
有了这个定理,就可以进行W e l z l WelzlWelzl算法了。先取3个点建立一个圆(不共线的三个点能确定一个圆,如果共线就取距离最远的两个点作为直径建立圆),然后遍历剩下的所有点,对于一个遍历到的点k kk:
- 如果该点在圆内,那么最小覆盖圆不变。
- 如果该点在圆外,根据上述定理,该点一定在想要求得的最小覆盖圆上,又因为三个点才能确定一个圆,所以需要枚举k kk之前的点来找剩下的两个点。直到找到两个点,使得与k kk确定的圆能够将k kk之前的所有点包在里面(或者在圆上)。
写成代码就是:
// a[] 存放需要求最小圆覆盖的所有点,n 为点的数量
// radius 为圆半径,center为圆心,都是全局变量
// point_in 函数判断一个点是否在最小覆盖圆内
// circle_center 函数有两个重载,一个用两个点确定圆,一个用三个点确定圆
// dist 函数计算距离
void min_circle_cover(point a[], int n)
{
radius = 0;
center = a[0];
for (int i = 1; i < n; i++) {
if (!point_in(a[i])) {
center = a[i];
radius = 0;
for (int j = 0; j < i; j++) {
if (!point_in(a[j])) {
circle_center(a[i], a[j], center);
radius = dist(a[j], center);
for (int k = 0; k < j; k++) {
if (!point_in(a[k])) {
circle_center(a[i], a[j], a[k], center);
radius = dist(a[k], center);
}
}
}
}
}
}
}
关于这个期望线性复杂度,我并不会证明。
只要调用m i n _ c i r c l e _ c o v e r min\_circle\_covermin_circle_cover之前调用r a n d o m _ s h u f f l e random\_shufflerandom_shuffle随机排列一下,就可以保证期望线性复杂度。
这里给一个完整代码(洛谷p1742的AC代码):
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const double eps = 1e-8;
int cmp(double x) {
if (fabs(x) < eps) return 0;
if (x > 0) return 1;
return -1;
}
struct point {
double x, y;
point() {}
point(double a, double b) : x(a), y(b) {}
friend point operator-(const point& a, const point& b) {
return point(a.x - b.x, a.y - b.y);
}
double norm() {
return sqrt(pow(x, 2) + pow(y, 2));
}
}p[maxn];
double dist(const point& a, const point& b) {
return (a - b).norm();
}
void circle_center(point p0, point p1, point& cp) {
cp.x = (p0.x + p1.x) / 2;
cp.y = (p0.y + p1.y) / 2;
}
void circle_center(point p0, point p1, point p2, point& cp) {
double a1 = p1.x - p0.x, b1 = p1.y - p0.y, c1 = (a1 * a1 + b1 * b1) / 2;
double a2 = p2.x - p0.x, b2 = p2.y - p0.y, c2 = (a2 * a2 + b2 * b2) / 2;
double d = (a1 * b2 - a2 * b1);
cp.x = p0.x + (c1 * b2 - c2 * b1) / d;
cp.y = p0.y + (a1 * c2 - a2 * c1) / d;
}
double radius; point center;
bool point_in(const point& p) {
return cmp(dist(p, center) - radius) < 0;
}
void min_circle_cover(point a[], int n)
{
radius = 0;
center = a[0];
for (int i = 1; i < n; i++) if (!point_in(a[i])) {
center = a[i], radius = 0;
for (int j = 0; j < i; j++) if (!point_in(a[j])) {
circle_center(a[i], a[j], center);
radius = dist(a[j], center);
for (int k = 0; k < j; k++) if (!point_in(a[k])) {
circle_center(a[i], a[j], a[k], center);
radius = dist(a[k], center);
}
}
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
double x, y;
scanf("%lf%lf", &x, &y);
p[i] = point(x, y);
}
random_shuffle(p, p + n);
min_circle_cover(p, n);
printf("%.10lf\n%.10lf %.10lf", radius, center.x, center.y);
return 0;
}
版权声明:本文为qq_37304699原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。