问题虫洞: Minimal Circle ZOJ - 1450
黑洞内窥:
给出n个点,求最小圆覆盖完所有的点,
输出圆心座标、半径;
最小圆覆盖问题:推荐博客:最小圆覆盖
算法步骤:
最优解的圆一定是以某两个点连线为直径的圆 或 某三个点组成的三角形的外接圆
初始将圆心定为第一个点,R=0
1.枚举第一个点 i,若点 i 不在目前圆内,设它为圆心,进入2
2.再枚举第二个点 j,若点 j 不在当前圆内,设当前圆为以 i,j 为直径的圆,进入3
3.枚举第三个点 k,若点 k 不在当前圆内,设当前圆为 i,j,k 的外接圆
看似O(n3)的复杂度,但点集随机打乱后期望复杂度O(n)。
random_shuffle函数的作用:
将[first,last)的元素次序随机重排。N个元素的排列方式共有N!种,
random_shuffle会产生一个均匀分布,因此任何一个排列被选中的概率为1/N!。
三点定圆的推导:
上面的模板题ACcode:
#include<stdio.h>
#include<iostream>
#include<map>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
#define MAXN 100005
#define INF 0x3f3f3f3f//将近int类型最大数的一半,而且乘2不会爆int
#define MOD 1000000007 // MOD%4 = 3
const double pi = acos(-1.0);
const double eps = 1e-6;
int n;
double r; ///r为最小圆半径,n为所有覆盖点点数
struct point
{
double x, y;
}a[MAXN], o; ///o为最小圆的圆心座标
double distance(point p1, point p2)
{
return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
}
void circum(point p1, point p2, point p3)///三点定圆
{
double a, b, c, d, e, f;
a = 2*(p2.x - p1.x);
b = 2*(p2.y - p1.y);
c = p2.x*p2.x + p2.y*p2.y - p1.x*p1.x - p1.y*p1.y;
d = 2*(p3.x - p1.x);
e = 2*(p3.y - p1.y);
f = p3.x*p3.x + p3.y*p3.y - p1.x*p1.x - p1.y*p1.y;
o.x = (b*f - e*c)/(b*d - e*a);
o.y = (d*c - a*f)/(b*d - e*a);
r = distance(p1, o);
}
void mincir() ///最小圆覆盖
{
o=a[1], r=0;
for(int i=2; i<=n; ++i)
{
if(distance(o, a[i]) > r+eps)
{
o=a[i], r=0;
for(int j=1; j<=i-1; ++j)
{
if(distance(o, a[j]) > r+eps)
{
o.x = (a[i].x+a[j].x)/2;
o.y = (a[i].y+a[j].y)/2;
r = distance(o, a[j]);
for(int k=1; k<=j-1; ++k)
if(distance(o, a[k]) > r+eps)
circum(a[i], a[j], a[k]);
}
}
}
}
}
int main()
{
while(cin >> n && n)
{
for(int i=1; i<=n; ++i)
scanf("%lf %lf", &a[i].x, &a[i].y);
random_shuffle(a+1,a+1+n); ///随机重拍,这里可不要。
mincir();
printf("%.2f %.2f %.2f\n", o.x, o.y, r);
}
return 0;
}