[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;
}

  

posted @ 2015-03-19 14:49  JoeFan  阅读(374)  评论(0编辑  收藏  举报