洛谷P1742:最小圆覆盖,计算几何,贪心随机保证复杂度

本文介绍了一个经典的几何问题——最小圆覆盖算法,旨在寻找一个尽可能小的圆来覆盖平面上的一组给定点。通过分析不同点数量对圆大小的影响,文章提供了一个接近线性的高效算法实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正题

      最小圆覆盖是一个模板。

      在n个点中,构造一个尽量小的圆,使其覆盖所有的点。

      很明显,如果边上有一个点或者两个点,那么圆的大小可以再缩小。边上三个点的时候就刚刚好可以固定一个圆。

      那么我们很容易就可以打出一个n的三次方的模板,只要经过一定的优化就可以打出下面的模板。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;

int n;
struct node{
	double x,y;
}s[100010],ci;
double r=0;

double dou(double x){
	return x*x;
}

double get_dis(node x,node y){
	return sqrt(dou(x.x-y.x)+dou(x.y-y.y));
}

int calc_c(double a,double b,double c,double d,double e,double f){
	ci.x=(e*d-b*f)/(a*d-b*c);
	ci.y=(e*c-a*f)/(b*c-a*d);
	return 1;
}

bool uninside(node x){
	return get_dis(ci,x)>r;
}

void get_ans(){
	ci.x=0,ci.y=0;r=0;
	for(int i=1;i<=n;i++){
		if(!uninside(s[i])) continue; 
		ci=s[i];
		r=0;
		for(int j=1;j<i;j++)
			if(uninside(s[j])){
				ci.x=(s[i].x+s[j].x)/2;
				ci.y=(s[i].y+s[j].y)/2;
				r=get_dis(ci,s[i]);
				for(int k=1;k<j;k++)
					if(uninside(s[k])) calc_c(s[i].x-s[j].x,s[i].y-s[j].y,s[i].x-s[k].x,
					s[i].y-s[k].y,(dou(s[i].x)-dou(s[j].x)-dou(s[j].y)+dou(s[i].y))/2,
					(dou(s[i].x)-dou(s[k].x)-dou(s[k].y)+dou(s[i].y))/2),r=get_dis(ci,s[i]);
			}
	}
	printf("%.10lf\n%.10lf %.10lf",r,ci.x,ci.y);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%lf %lf",&s[i].x,&s[i].y);
	random_shuffle(s+1,s+1+n);
	get_ans();
}

      里面那个长长的就是已知三个点,求圆的方程,不会的可以自己推一推,在这里不多说。

      看起来好像是n的三次方的复杂度,其实随机上来说,复杂度是接近n的。

      因为一个点不在 能覆盖前面的圆 几率只是3/i,因为第i个点在那之外,它只会替换那三个点中的任意一个点。而剩下的几率就是不替换,所以几率是3/i;

      那么倒数第二层的进入几率就是3/i,每次复杂度是j,所以一共3n,否则就是1的复杂度。

      加起来接近线性。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值