3028. 最小圆覆盖

题目链接

3028. 最小圆覆盖
在一个二维平面上给定 \(N\) 个点,请你画出一个最小的能够包含所有点的圆。

圆的边上的点视作在圆的内部。

输入格式

第一行包含一个整数 \(N\)

接下来 \(N\) 行,每行包含两个实数,表示一个点的坐标 \((X_i,Y_i)\)

输出格式

第一行输出圆的半径。

第二行输出圆心的坐标。

结果保留 \(10\) 位小数。

数据范围

\(2 \le N \le 10^5\),
\(-10000.0 \le X_i,Y_i \le 10000.0\)

输入样例:

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

输出样例:

5.0000000000
5.0000000000 5.0000000000

解题思路

最小圆覆盖

最小圆覆盖是一种随机化算法,其有两条重要的性质:

  • 最小覆盖圆唯一

  • 若点 \(P\) 不在点集 \(S\) 的最小覆盖圆内部,则点 \(P\) 必在 \(P\cup S\) 的最小覆盖圆边上

利用这两条性质可以采用随机增量算法解决最小覆盖圆问题,即假设现在已经求出了 \(1\sim i-1\) 的最小覆盖圆,且此时 \(i\) 这个点在圆外,则 \(i\) 必定在前 \(i\) 个点构成的最小覆盖圆上,即找到了前 \(i\) 个点构成的最小覆盖圆的其中一个点,还需要找到另外两个点,对于前 \(i\) 个点,假设已经求出 \(1\sim j-1\)\(i\) 这个点在边上的最小覆盖圆,此时 \(j\) 在这样的最小覆盖圆外部,不难证明此时 \(j\) 在前 \(j\) 个点且 \(i\) 在边上的最小覆盖圆的边上,这时找到了两个点 \(i,j\),同理一开始先构建这两个点的最小覆盖圆,对于前 \(j\) 个点,假设已经求出 \(1\sim k-1\)\(i,j\) 这两个点在边上的最小覆盖圆,此时 \(k\) 在这样的最小覆盖圆外部,这时找到三个点 \(i,j,k\) 即可构成前 \(i\) 个点的最小覆盖圆

时间复杂度证明:
对于内层循环来说,其时间复杂度:\(O(n)\),这三个点是关键点,对于中层循环,其有 \(\frac{3}{n}\) 的概率到达内层循环,即时间复杂度为 \(O(n+\frac{3}{n}\times n)\),外层循环同理,故:

  • 时间复杂度:\(O(n)\)

代码

// Problem: 最小圆覆盖
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/3031/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1e5+5;
const double eps=1e-12,pi=acos(-1);
typedef pair<double,double> PDD;
int n;
PDD a[N];
struct Circle
{
	PDD p;
	double r;
};
PDD operator+(PDD a,PDD b)
{
	return {a.fi+b.fi,a.se+b.se};
}
PDD operator-(PDD a,PDD b)
{
	return {a.fi-b.fi,a.se-b.se};
}
PDD operator*(PDD a,double t)
{
	return {a.fi*t,a.se*t};
}
double operator*(PDD a,PDD b)
{
	return a.fi*b.se-b.fi*a.se;
}
PDD operator/(PDD a,double t)
{
	return {a.fi/t,a.se/t};
}
int sign(double x)
{
	if(fabs(x)<eps)return 0;
	if(x<0)return -1;
	return 1;
}
int dcmp(double x,double y)
{
	if(fabs(x-y)<0)return 0;
	if(x<y)return -1;
	return 1;
}

double get_dist(PDD a,PDD b)
{
	return sqrt((a.fi-b.fi)*(a.fi-b.fi)+(a.se-b.se)*(a.se-b.se));
}
PDD rotate(PDD a,double angle)
{
	return {a.fi*cos(angle)+a.se*sin(angle),-a.fi*sin(angle)+a.se*cos(angle)};
}
pair<PDD,PDD> get_line(PDD a,PDD b)
{
	return {(a+b)/2,rotate(b-a,pi/2)};
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w)
{
	PDD u=p-q;
	double t=w*u/(v*w);
	return p+v*t;
}
Circle get_circle(PDD a,PDD b,PDD c)
{
	auto u=get_line(a,b),v=get_line(a,c);
	PDD p=get_line_intersection(u.fi,u.se,v.fi,v.se);
	return {p,get_dist(p,a)};
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].fi,&a[i].se);
    random_shuffle(a+1,a+1+n);
    Circle c={a[1],0};
    for(int i=2;i<=n;i++)
    	if(dcmp(c.r,get_dist(c.p,a[i]))<0)
    	{
    		c={a[i],0};
    		for(int j=1;j<i;j++)
    			if(dcmp(c.r,get_dist(c.p,a[j]))<0)
    			{
    				c={(a[i]+a[j])/2,get_dist(a[i],a[j])/2};
    				for(int k=1;k<j;k++)
    					if(dcmp(c.r,get_dist(c.p,a[k]))<0)
    					{
    						c=get_circle(a[i],a[j],a[k]);
    					}
    			}
    	}
    printf("%.10lf\n%.10lf %.10lf",c.r,c.p.fi,c.p.se);
    return 0;
}
posted @ 2022-11-23 13:51  zyy2001  阅读(54)  评论(0编辑  收藏  举报