题目链接
3028. 最小圆覆盖
在一个二维平面上给定 N N 个点,请你画出一个最小的能够包含所有点的圆。
圆的边上的点视作在圆的内部。
输入格式
第一行包含一个整数 N N 。
接下来 N N 行,每行包含两个实数,表示一个点的坐标 ( X i , Y i ) ( X i , Y i ) 。
输出格式
第一行输出圆的半径。
第二行输出圆心的坐标。
结果保留 10 10 位小数。
数据范围
2 ≤ N ≤ 10 5 2 ≤ N ≤ 10 5 ,
− 10000.0 ≤ X i , Y i ≤ 10000.0 − 10000.0 ≤ X i , Y i ≤ 10000.0
输入样例:
输出样例:
解题思路
最小圆覆盖
最小圆覆盖是一种随机化算法,其有两条重要的性质:
利用这两条性质可以采用随机增量算法解决最小覆盖圆问题,即假设现在已经求出了 1 ∼ i − 1 1 ∼ i − 1 的最小覆盖圆,且此时 i i 这个点在圆外,则 i i 必定在前 i i 个点构成的最小覆盖圆上,即找到了前 i i 个点构成的最小覆盖圆的其中一个点,还需要找到另外两个点,对于前 i i 个点,假设已经求出 1 ∼ j − 1 1 ∼ j − 1 且 i i 这个点在边上的最小覆盖圆,此时 j j 在这样的最小覆盖圆外部,不难证明此时 j j 在前 j j 个点且 i i 在边上的最小覆盖圆的边上,这时找到了两个点 i , j i , j ,同理一开始先构建这两个点的最小覆盖圆,对于前 j j 个点,假设已经求出 1 ∼ k − 1 1 ∼ k − 1 且 i , j i , j 这两个点在边上的最小覆盖圆,此时 k k 在这样的最小覆盖圆外部,这时找到三个点 i , j , k i , j , k 即可构成前 i i 个点的最小覆盖圆
时间复杂度证明:
对于内层循环来说,其时间复杂度:O ( n ) O ( n ) ,这三个点是关键点,对于中层循环,其有 3 n 3 n 的概率到达内层循环,即时间复杂度为 O ( n + 3 n × n ) O ( n + 3 n × n ) ,外层循环同理,故:
代码
#include <bits/stdc++.h>
#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 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!