[BZOJ 1336] [Balkan2002] Alien最小圆覆盖 【随机增量法】
题目链接:BZOJ - 1336
题目分析
最小圆覆盖有一个算法叫做随机增量法,看起来复杂度像是 O(n^3) ,但是可以证明其实平均是 O(n) 的,至于为什么我不知道= =
为什么是随机呢?因为算法进行前要将所有的点 random_shuffle 一次。为什么要这样做呢?因为这样就可以防止出题人用最坏情况卡掉增量算法。
这和随机化快排使用随机是一个道理。
算法步骤:
random_shuffle n 个点 将圆设定为以 P[1] 为圆心,以 0 为半径 for i : 1 to n { if (P[i] 不在圆内) { 将圆设定为以 P[i] 为圆心,以 0 为半径 for j : 1 to i - 1 { if (P[j] 不在圆内) { 将圆设定为以 P[i]P[j] 为直径 for k : 1 to j - 1 { if (P[k] 不在圆内) { 将圆设定为 P[i],P[j],P[k] 的外接圆 } } } } } }
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define Vector Point typedef double LF; const int MaxN = 100000 + 5; const LF Eps = 1e-12; int n; struct Point { LF x, y; Point() {} Point(LF a, LF b) { x = a; y = b; } } P[MaxN]; Point operator + (Point p1, Point p2) { return Point(p1.x + p2.x, p1.y + p2.y); } Point operator - (Point p1, Point p2) { return Point(p1.x - p2.x, p1.y - p2.y); } Vector operator * (Vector v, LF t) { return Vector(v.x * t, v.y * t); } Vector operator / (Vector v, LF t) { return Vector(v.x / t, v.y / t); } inline LF Sqr(LF x) {return x * x;} inline LF gmax(LF a, LF b) {return a > b ? a : b;} inline LF Dis(Point p1, Point p2) { return sqrt(Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y)); } struct Circle { Point o; LF r; Circle() {} Circle(Point a, LF b) { o = a; r = b; } bool Inside(Point p) { return Dis(p, o) - r <= Eps; } } C; struct Line { Point p; Vector v; Line() {} Line(Point a, Vector b) { p = a; v = b; } } L1, L2; LF Cross(Vector v1, Vector v2) { return v1.x * v2.y - v2.x * v1.y; } Point Intersection(Line l1, Line l2) { Vector u = l2.p - l1.p; LF t = Cross(l2.v, u) / Cross(l2.v, l1.v); return l1.p + (l1.v * t); } Vector Change(Vector v) { return Vector(-v.y, v.x); } Line Verticle(Point p1, Point p2) { Line ret; ret.p = (p1 + p2) / 2.0; ret.v = Change(p2 - p1); return ret; } int main() { srand(19981014); scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%lf%lf", &P[i].x, &P[i].y); random_shuffle(P + 1, P + n + 1); C.o = P[1]; C.r = 0; for (int i = 1; i <= n; ++i) { if (C.Inside(P[i])) continue; C.o = P[i]; C.r = 0; for (int j = 1; j < i; ++j) { if (C.Inside(P[j])) continue; C.o = (P[i] + P[j]) / 2.0; C.r = Dis(C.o, P[j]); for (int k = 1; k < j; ++k) { if (C.Inside(P[k])) continue; L1 = Verticle(P[i], P[k]); L2 = Verticle(P[j], P[k]); C.o = Intersection(L1, L2); C.r = Dis(C.o, P[k]); } } } printf("%.10lf\n%.10lf %.10lf\n", C.r, C.o.x, C.o.y); return 0; }