HDU3995 最小包围圆变形

本文介绍了一个高效的最小包围圆算法实现,特别适用于两个点集的情况。该算法采用随机增量法达到O(n)的时间复杂度,并详细展示了核心代码实现,包括如何处理不同数量的点来确定包围圆。

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

这题出2了,下次注意点。

时间卡紧了导致一些不太好的最小包围圆模板过不了。

精度上要求有点高,可能导致了一些wa。

有的队方法完全正确,由于出题的经验不足,可惜了。

直接把标程贴出来吧。


题意:两个点集的最小包围圆,  随机增量的时候,改一下模板就行了。

/*
*最小包围圆随机增量O(n)变形
* by tju_accry
*
*本机测试时间:
*real	0m0.957s
*user	0m0.944s
*sys	0m0.012s

*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

#define sqr(a) ((a)*(a))
#define trarea(p) fabs ((vdet(p[0],p[1],p[2])/2.0))
double const EPS = 1e-12;
int const maxn = 20011;
struct point{double x,y;int flag;};
struct circle{
		point c;
		double r;
};
inline double vdet(point op,point p1,point p2){
		return (p1.x-op.x)*(p2.y-op.y)-(p2.x-op.x)*(p1.y-op.y);
}
inline double dis(point a,point b){
		return sqr(a.x-b.x)+sqr(a.y-b.y);
}
inline point get_out_circle(point p[]){  //三角形外接圆圆心
		double c1,c2,xa,xb,xc,ya,yb,yc;
		point o;
		xa=p[0].x,xb=p[1].x,xc=p[2].x;
		ya = p[0].y,yb = p[1].y,yc = p[2].y;
		c1 = (sqr(xa) + sqr(ya) - sqr(xb) - sqr(yb))/2.0;
		c2 = (sqr(xa) + sqr(ya) - sqr(xc) - sqr(yc))/2.0;
		o.x = (c1*(ya-yc)-c2*(ya-yb))/((xa-xb)*(ya-yc)-(xa-xc)*(ya-yb));
		o.y = (c1*(xa-xc)-c2*(xa-xb))/((ya-yb)*(xa-xc)-(ya-yc)*(xa-xb));
		return o;
}
inline double get_out_r(point p[]){  //三角形外接圆半径
		double a = dis(p[0],p[1]),b = dis(p[1],p[2]),c = dis(p[2],p[0]),s = trarea(p);
		return a*b*c/sqr(4*s);
}
point must_on[3],p[maxn];
circle mc;
inline void get_circle(int tm){  //一点,两点圆,三点圆
		switch(tm){
				case 0:mc.r = -1;
					   break;
				case 1:mc.r = 0,mc.c = must_on[0];
					   break;
				case 2:{
							   mc.r = dis(must_on[0],must_on[1])/4.0;
							   mc.c.x = (must_on[0].x+must_on[1].x)/2.0;
							   mc.c.y = (must_on[0].y + must_on[1].y)/2.0;
					   }break;
				case 3:{
							   mc.r = get_out_r(must_on);
							   mc.c = get_out_circle(must_on);
					   }break;
		}
}
inline void min_circle(int t,int ton){
		get_circle(ton);
		if(ton >= 3)return;
		for(int i = 0; i < t; ++i){
				/*
				//求一个点集的最小包围圆
				if(dis(mc.c,p[i]) > mc.r + EPS){
					must_on[ton] = p[i];
					min_circle(i,ton+1);
				}
				*/

				/*本题*/
				if(p[i].flag == 1){
					if(dis(mc.c,p[i]) > mc.r + EPS){  //A集合点在圆内或圆周上
						must_on[ton] = p[i];
						min_circle(i,ton+1);
					}
				}
				else {
					if(dis(mc.c,p[i]) + EPS < mc.r){  //B集合的点不在圆内(可以在圆周上)
						must_on[ton] = p[i];
						min_circle(i,ton+1);
					}
				}

		}
}
int main(){
		int n,m;
		//freopen("data.in","r",stdin);
		//freopen("data.out","w",stdout);
		while (scanf("%d%d",&n,&m) != EOF){
				for(int i = 0; i < n; ++i){
						scanf("%lf%lf",&p[i].x,&p[i].y);
						p[i].flag = 1;
				}

				for(int i = n; i < m + n; ++i){
						scanf("%lf%lf",&p[i].x,&p[i].y);
						p[i].flag = 2;
				}
				n = n + m;
				random_shuffle(p,p+n); //随机化序列元素
				min_circle(n,0);
				printf("%.3lf %.3lf\n%.3lf\n",mc.c.x,mc.c.y,sqrt(mc.r));
		}
		return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值