1. 程式人生 > >【BZOJ1336】[Balkan2002]Alien最小圓覆蓋 隨機增量法

【BZOJ1336】[Balkan2002]Alien最小圓覆蓋 隨機增量法

tmp style i++ 5.1 logs eps light rip 個數

【BZOJ1336】[Balkan2002]Alien最小圓覆蓋

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

題解:特地學了一發隨機增量法,期望復雜度據說是O(n),但不會證。

起初以1為圓心。先枚舉一個點,如果這個點在圓外,則改為以哪個點為圓心;再枚舉一個點,如果這個點再圓外,則圓心改為這兩個點的中點;再枚舉一個點,如果這個點再圓外,則將圓心改為三角形的內心。如何求內心?本人懶得推式子所以寫的高斯消元。

由於是期望復雜度所以一開始要將原序列隨機排序。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=500010;
const double eps=1e-12;
struct point
{
	double x,y;
	point() {}
	point(double a,double b){x=a,y=b;}
}p[maxn],O;
int n;
double v[10][10],R;
point calc(point a,point b,point c)
{
	v[1][1]=2*b.x-2*a.x;
	v[1][2]=2*b.y-2*a.y;
	v[1][3]=b.x*b.x-a.x*a.x+b.y*b.y-a.y*a.y;
	v[2][1]=2*c.x-2*b.x;
	v[2][2]=2*c.y-2*b.y;
	v[2][3]=c.x*c.x-b.x*b.x+c.y*c.y-b.y*b.y;
	int i,j,k;
	for(i=1;i<=2;i++)
	{
		for(j=i+1;j<=2;j++)	if(fabs(v[j][i])>fabs(v[i][i]))	for(k=i;k<=3;k++)	swap(v[i][k],v[j][k]);
		double tmp=v[i][i];
		for(k=i;k<=3;k++)	v[i][k]/=tmp;
		for(j=1;j<=2;j++)	if(i!=j)	for(tmp=v[j][i],k=i;k<=3;k++)	v[j][k]-=tmp*v[i][k];
	}
	point ret(v[1][3],v[2][3]);
	return	ret;
}
double dis(point a,point b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main()
{
	scanf("%d",&n);
	int i,j,k;
	for(i=1;i<=n;i++)	scanf("%lf%lf",&p[i].x,&p[i].y);
	random_shuffle(p+1,p+n+1);
	O=p[1],R=0;
	for(i=2;i<=n;i++)	if(dis(O,p[i])>R+eps)
	{
		O=p[i],R=0;
		for(j=1;j<i;j++)	if(dis(O,p[j])>R+eps)
		{
			O=point((p[i].x+p[j].x)/2,(p[i].y+p[j].y)/2),R=dis(O,p[i]);
			for(k=1;k<j;k++)	if(dis(O,p[k])>R+eps)
				O=calc(p[i],p[j],p[k]),R=dis(O,p[k]);
		}
	}
	printf("%.2lf\n%.2lf %.2lf",R,O.x,O.y);
	return 0;
}

【BZOJ1336】[Balkan2002]Alien最小圓覆蓋 隨機增量法