3028. 最小圆覆盖

题目链接

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

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

输入格式

第一行包含一个整数 N

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

输出格式

第一行输出圆的半径。

第二行输出圆心的坐标。

结果保留 10 位小数。

数据范围

2N105,
10000.0Xi,Yi10000.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 必在 PS 的最小覆盖圆边上

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

时间复杂度证明:
对于内层循环来说,其时间复杂度:O(n),这三个点是关键点,对于中层循环,其有 3n 的概率到达内层循环,即时间复杂度为 O(n+3n×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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16918058.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示