[BZOJ 3564] [SHOI2014] 信号增幅仪 【最小圆覆盖】

题目链接:BZOJ - 3564

 

题目分析

求最小椭圆覆盖,题目给定了椭圆的长轴与 x 轴正方向的夹角,给定了椭圆长轴与短轴的比值。

那么先将所有点旋转一个角度,使椭圆长轴与 x 轴平行,再将所有点的 x 坐标除以长轴与短轴的比值,然后就直接做最小圆覆盖了。

随机增量法,一定别忘了 random_shuffle 。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

#define PI 3.14159265358979323846
#define Vector Point

typedef double LF;

const int MaxN = 50000 + 5;

const LF Eps = 1e-9;

int n;

LF Alpha, SinA, CosA;

struct Point
{
	LF x, y;
	
	Point() {}
	Point(LF a, LF b)
	{
		x = a; y = b;
	}
	
	void Rotate(LF SinA, LF CosA)
	{
		LF tx, ty;
		tx = CosA * x - SinA * y;
		ty = SinA * x + CosA * y;
		x = tx; y = ty;
	}
} P[MaxN];

inline LF Sqr(LF x) {return x * x;}

inline LF Dis(Point p1, Point p2)
{
	return sqrt(Sqr(p1.x - p2.x) + Sqr(p1.y - p2.y));
}

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);}
Point operator * (Point p, LF t) {return Point(p.x * t, p.y * t);}
Point operator / (Point p, LF t) {return Point(p.x / t, p.y / t);}
  
struct Line
{
	Point p;
	Vector v;
	
	Line() {}
	Line(Point p1, Point p2)
	{
		p = p1;
		v = p2 - p1;
	}
} L1, L2; 

struct Circle
{
	Point o;
	LF r;
	
	bool InCircle(Point p)
	{
		return Dis(o, p) <= r + Eps;
	}
} C;

inline 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(u, l2.v) / Cross(l1.v, l2.v);
	return l1.p + (l1.v * t); 
}

Vector Turn90(Vector v)
{
	return Vector(-v.y, v.x);
}

Line Verticle(Point p1, Point p2)
{
	Line ret;
	ret.p = (p1 + p2) / 2.0;
	ret.v = Turn90(p2 - p1);
	return ret;
}

int main()
{
	srand(51405102);
	scanf("%d", &n);
	int a, b;
	for (int i = 1; i <= n; ++i) 
	{
		scanf("%d%d", &a, &b);
		P[i] = Point((LF)a, (LF)b);
	}
	random_shuffle(P + 1, P + n + 1);
	int ad, p;
	scanf("%d", &ad);
	Alpha = (LF)(-ad) / (LF)180 * PI;
	SinA = sin(Alpha); CosA = cos(Alpha);
	scanf("%d", &p);
	for (int i = 1; i <= n; ++i)
	{
		P[i].Rotate(SinA, CosA);
		P[i].x /= (LF)p;
	}
	C.o = P[1]; C.r = 0;
	for (int i = 1; i <= n; ++i)
	{
		if (C.InCircle(P[i])) continue;
		C.o = P[i]; C.r = 0;
		for (int j = 1; j < i; ++j)
		{
			if (C.InCircle(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.InCircle(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("%.3lf\n", C.r);
	return 0;
}

  

posted @ 2015-04-15 16:02  JoeFan  阅读(449)  评论(0编辑  收藏  举报