【BZOJ 1336&1337】 [Balkan2002]Alien最小圆覆盖

1336: [Balkan2002]Alien最小圆覆盖

Time Limit: 1 Sec   Memory Limit: 162 MBSec   Special Judge
Submit: 1028   Solved: 470
[ Submit][ Status][ Discuss]

Description

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

Input

先给出点的个数N,2<=N<=100000,再给出坐标Xi,Yi.(-10000.0<=xi,yi<=10000.0)

Output

输出圆的半径,及圆心的坐标

Sample Input

6
8.0 9.0
4.0 7.5
1.0 2.0
5.1 8.7
9.0 2.0
4.5 1.0


Sample Output

5.00
5.00 5.00

随机增量法求最小圆覆盖。


假设已经求出前i个点的最小覆盖圆,加入第i+1个点的时候,如果第i+1个点不在之前求出的圆内,那么这个点一定在新圆的边界上,用i+1和之前随便一个点构成新圆。


再从1-i枚举,找到不在这个新圆中的点j,那么i+1,j一定在更新的圆的边界上。


再从1-j-1枚举,找到不在当前圆中的点k。


三点确定一个圆,因此i+1,j,k构成了新的圆,能覆盖前k个点。


这样的枚举一定能保证最后所有点都被覆盖(枚举了所有点对)且圆最小(点在圆的边界)。


看起来这个做法是O(n^3),但是在随机数据下是O(n),大概是因为一个点不在当前圆的概率比较小。

证明:(from http://blog.csdn.net/lthyxy/article/details/6661250)



#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#define eps 1e-12
using namespace std;
int n;
double r;
struct Point
{
	double x,y;
	Point() {}
	Point (double x,double y): x(x),y(y) {}
	friend Point operator + (Point a,Point b)
	{
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator - (Point a,Point b)
	{
		return Point(a.x-b.x,a.y-b.y);
	}
	friend Point operator / (Point a,double p)
	{
		return Point(a.x/p,a.y/p);
	}
}p[100005],o;
double dis(Point a,Point b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
Point rev(Point x)
{
	return Point(-x.y,x.x);
}
Point Geto(Point a,Point b,Point c)
{
	Point x1=(a+b)/2,x2=(a+b)/2+rev(a-b),y1=(a+c)/2,y2=(a+c)/2+rev(a-c);
	if (fabs(y1.x-y2.x)<eps)
		swap(x1,y1),swap(x2,y2);
	double k2=(y1.y-y2.y)/(y1.x-y2.x),b2=y2.y-y2.x*k2;
	if (fabs(x1.x-x2.x)<eps)
		return Point(x1.x,k2*x1.x+b2);
	double k1=(x1.y-x2.y)/(x1.x-x2.x),b1=x2.y-k1*x2.x;
	double x=(b2-b1)/(k1-k2);
	return Point(x,x*k1+b1);
}
int main()
{
    scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%lf%lf",&p[i].x,&p[i].y);
	for (int i=1;i<=n;i++)
		swap(p[rand()%n+1],p[rand()%n+1]);
	o=p[1],r=0.0;
	for (int i=2;i<=n;i++)
	{
		if (dis(o,p[i])<r+eps) continue;
		o=(p[i]+p[1])/2,r=dis(p[1],o);
		for (int j=2;j<i;j++)
		{
			if (dis(o,p[j])<r+eps) continue;
			o=(p[i]+p[j])/2,r=dis(p[i],o);
			for (int k=1;k<j;k++)
			{
				if (dis(o,p[k])<r+eps) continue;
				o=Geto(p[i],p[j],p[k]);
				r=dis(p[i],o);
			}
		}
	}
	printf("%.10lf\n%.10lf %.10lf\n",r,o.x,o.y);
	return 0;
}
</span>




三点确定一圆完全用数学方法,求中垂线交点,略繁。。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值