学习笔记

最小圆覆盖问题是这样的:

给出 (N) 个点,让你画一个最小的包含所有点的圆。


首先,答案一定是一个由这些点里面的三个点所确定的圆,或者由其中的两个点作为直径的圆。

所以我们很容易得到一个 (O(n^4)) 的算法 - 枚举三个在圆上的点并判断有没有包含。显然只要存在一个包含了所有点的圆,就是最小圆了。(也就是说不需要判断是不是最小,

下面是一种在期望情况下为 (O(n)) 的算法 - 随机增量法。

这个算法基于一个这样的事实:对于任意的 (N) 个点,其中的一个点如果没有出现在其它的点的最小覆盖圆上,那么这个点一定出现在了所有 (N) 个点的最小覆盖圆的圆周上。

对于第 (i) 个点 (P_i),我们假设已经得到了一个包含前 (i - 1) 个点的圆 (C)

如果 (P_i) 已经在 (C) 中了,那么不需要考虑,直接跳过就可以了。

否则,(P_i) 一定在包含前 (i) 个点的圆的圆周上。我们将 (C) 重置为以 (P_i) 为圆心,(0) 为半径的圆,然后枚举 (j(1 leq j < i))

此时圆 (C) 应该表示的是包含前 (j-1) 个点和 (P_i) 的圆。

如果点 (P_j) 在圆 (C) 中,那么依然不需要考虑,继续枚举下一个 (j) 就可以了。

否则点 (P_j) 一定在包含前 (j) 个点和 (P_i) 的圆的圆周上。将圆 (C) 重置为以 (P_iP_j) 为直径的圆。

然后枚举 (k(1leq k < j)),此时圆 (C) 表示的应该是包含前 (k-1) 个点和点 (P_j, P_i) 的圆。

如果 (P_k)(C) 中,那么有也不要考虑。否则点 (P_k) 一定在包含前 (k)点和点 (P_j, P_i) 的圆的圆周上,直接将圆 (C) 重置为由点 (P_i, P_j, P_k) 确定的圆就可以了。


上面的的算法的复杂度看起来是 (O(n^3)) 的,实际上,我们可以证明,在期望情况下,它的复杂度是 (O(n)) 的。

首先,对于一个长度为 (n) 的顺序随机的点序列 (P),假设它们的最小覆盖圆为 (C),则对于 (forall P_i) 出现在 (C) 的圆周上的概率均等,又一共会有 (3) 个点出现在圆周上,那么每一个的出现在圆周上的概率为 (frac 3n)

然后,对于第 (i) 个点,若它没有出现在由前 (i-1) 个点的最小覆盖圆中,那么它一定在前 (i) 个点的最小覆盖圆的圆周上,如上文所述,这个概率为 (frac 3i)。因此,需要枚举 (j) 的情况只有 (frac 3i)。同理,对于 (j) 来说,需要枚举 (k) 的情况也只有 (frac 3j) 的概率。

因此在期望情况下,总的时间 (T(n) = sumlimits_{i=1}^n frac 3i sumlimits_{j=1}^{i=1} frac 3j cdot j = sumlimits_{i=1}^n frac 3i cdot 3i = 9n)。于是总期望时间复杂度为 (O(n))


模板题 - bzoj1336 [Balkan2002]Alien最小圆覆盖

直接用板子就可以了。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I>
inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 100000 + 7;
const double eps = 1e-10;

int n, m;

inline int dcmp(const double &x) { return fabs(x) < eps ? 0 : (x < 0 ? -1 : 1); }
struct Point {
	double x, y;
	inline Point(const double &x = 0, const double &y = 0) : x(x), y(y) {}
} a[N];

inline double dist(const Point &a, const Point &b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }

inline std::pair<Point, double> get_C(const Point &p1, const Point &p2, const Point &p3) {
	double a1 = 2 * (p1.x - p2.x), b1 = 2 * (p1.y - p2.y), c1 = (p1.x * p1.x + p1.y * p1.y) - (p2.x * p2.x + p2.y * p2.y);
	double a2 = 2 * (p2.x - p3.x), b2 = 2 * (p2.y - p3.y), c2 = (p2.x * p2.x + p2.y * p2.y) - (p3.x * p3.x + p3.y * p3.y);
	double x = (b1 * c2 - b2 * c1) / (a2 * b1 - a1 * b2), y = (a1 * c2 - a2 * c1) / (a1 * b2 - a2 * b1);
	double r = sqrt((p1.x - x) * (p1.x - x) + (p1.y - y) * (p1.y - y));
	return std::make_pair(Point(x, y), r);
}

inline void work() {
//	std::mt19937 rnd(time(0) + (ull)new char);
//	std::shuffle(a + 1, a + n + 1, rnd);
	srand(time(0) + (ull)new char);
	std::random_shuffle(a + 1, a + n + 1);
	Point O = a[1];
	double r = 0;
	for (int i = 2; i <= n; ++i) if (dcmp(dist(a[i], O) - r) > 0) {
		O = a[i], r = 0;
		for (int j = 1; j < i; ++j) if (dcmp(dist(a[j], O) - r) > 0) {
			O = Point((a[i].x + a[j].x) / 2, (a[i].y + a[j].y) / 2), r = dist(a[i], a[j]) / 2;
			for (int k = 1; k < j; ++k) if (dcmp(dist(a[k], O) - r) > 0) {
				const std::pair<Point, double> &tmp = get_C(a[i], a[j], a[k]);
				O = tmp.fi, r = tmp.se;
			}
		}
	}
	printf("%.2lf
", r);
	printf("%.2lf %.2lf
", O.x, O.y);
}

inline void init() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%lf%lf", &a[i].x, &a[i].y);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}

别的一些题目

[https://lydsy.com/JudgeOnline/problem.php?id=2280](BZOJ2280 [Poi2011]Plot) 题解


参考资料

  1. https://blog.csdn.net/niiick/article/details/89153096
  2. https://blog.csdn.net/wu_tongtong/article/details/79362339
  3. https://www.cnblogs.com/GXZlegend/p/7467029.html
原文地址:https://www.cnblogs.com/hankeke/p/Minimum-circle-covering-algorithm.html