SCUPI 第十四周编程作业 最小覆盖圆 题目笔记

叮叮~
萌新一枚呀
笔记仅供参考,具体还是要以上课讲授的内容为准
另外
左老师YYDS

题面

描述
给出平面上N(N <=10^5)个点。请求出一个半径最小的圆覆盖住所有的点。

输入
第一行给出数字N,接下来N行,每行两个实数x,y表示其坐标。
其中,-100000<=x,y<=100000
输出
第一行输出最小覆盖圆的圆心
第二行输出半径
输出保留三位小数.

测试数据

Case#1:
Input

4
1 0
0 1
0 -1
-1 0

Output

0.000 0.000
1.000

另外附上一组本人程序跑出来的测试数据
(有的小伙伴跑出来nan 这时候可以去提交混15分 嗨嗨嗨)
Case#2:
Input

20
0 0
1 5
2 6
-1 3
4 6
2 8
5 4
3 9
7 7
5 9
2 3
3 5
1 2
3 0
-2 -3
-3 -5
-6 -9
-1 -3
-5 -6
-7 -1

output

-0.500 0.000
10.548

Case#3:
Input

5
-0.5 0
0.5 0
0 0.866
-0.25 0.217
0.3 0.207

Output

-0.000 0.289
0.577

题解
在计算机中表示点常用点对,表示直线常用点和方向向量,以此来在程序中表述
但是本题由于实在是没思路,所以就没用
这里没使用随机分布的方法来简化最小覆盖圆的计算的时间复杂度
这里只给出一种办法
大概步骤:
第一层循环 枚举第一个点
第二层循环 枚举第二个点
判断第二个点和圆的关系
这时构造两点共圆
两点不能确定圆
但是我们可以知道,以这两点作为直径的时候,圆是最小的
因此如此做圆
第三层循环 枚举第三个点
判断是不是在圆上or圆内
然后选择是不是构造三点出圆

每次都会判断是否进行了覆盖 恰好完全覆盖时则会最小
可以用随机函数,将点的分布随机化,这样得到最小覆盖圆的速度其实会更快,时间复杂度接近O(n)而不是严格的O(n^3)
因此本题关键在于求 两点求出的圆心和半径 以及 三点求出的圆心和半径

这是输入三个点求横纵坐标的两个函数
用到了行列式进行推导
最终获得的化简结果
返回值即使横坐标与纵坐标
输入时,采用a[] b[]两个数组分别存储横纵坐标=-=

double solve3xpoints(double x1,double y1,double x2,double y2,double x3,double y3){
	double a=x1-x2;
	double b=y1-y2;
	double c=x1-x3;
	double d=y1-y3;
	double e=((x1*x1-x2*x2)-(y2*y2-y1*y1))/2;
	double f=((x1*x1-x3*x3)-(y3*y3-y1*y1))/2;
	return -(d*e-b*f)/(b*c-a*d);
}
double solve3ypoints(double x1,double y1,double x2,double y2,double x3,double y3){
	double a=x1-x2;
	double b=y1-y2;
	double c=x1-x3;
	double d=y1-y3;
	double e=((x1*x1-x2*x2)-(y2*y2-y1*y1))/2;
	double f=((x1*x1-x3*x3)-(y3*y3-y1*y1))/2;
	return -(a*f-c*e)/(b*c-a*d);
}

下面这个是比较浮点数的常用方法
因为会有精度问题
所以经常采用下列方法比较

int cmpd(double a,double b){
	if(fabs(a-b)<1e-8)
		return 0;
	if(a<b)
		return -1;
	return 1;
}

下面是两点确定圆心横纵坐标的代码

double solvex2points(double x1,double x2){
	return (x1+x2)/2;
}
double solvey2points(double y1,double y2){
	return (y1+y2)/2;
}

下面是求两点距离

double solve2pointsdis(double x1,double y1,double x2,double y2){
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

用结构体储存最后的圆(好看)
AC代码

#include<iostream>
#include<math.h> 
using namespace std;
const int N=100001;
double a[N],b[N];
struct Circle{
	double x;
	double y;
	double r;
};
double solvex2points(double x1,double x2){
	return (x1+x2)/2;
}
double solvey2points(double y1,double y2){
	return (y1+y2)/2;
}
double solve2pointsdis(double x1,double y1,double x2,double y2){
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int cmpd(double a,double b){
	if(fabs(a-b)<1e-8)
		return 0;
	if(a<b)
		return -1;
	return 1;
}
double solve3xpoints(double x1,double y1,double x2,double y2,double x3,double y3){
	double a=x1-x2;
	double b=y1-y2;
	double c=x1-x3;
	double d=y1-y3;
	double e=((x1*x1-x2*x2)-(y2*y2-y1*y1))/2;
	double f=((x1*x1-x3*x3)-(y3*y3-y1*y1))/2;
	return -(d*e-b*f)/(b*c-a*d);
}
double solve3ypoints(double x1,double y1,double x2,double y2,double x3,double y3){
	double a=x1-x2;
	double b=y1-y2;
	double c=x1-x3;
	double d=y1-y3;
	double e=((x1*x1-x2*x2)-(y2*y2-y1*y1))/2;
	double f=((x1*x1-x3*x3)-(y3*y3-y1*y1))/2;
	return -(a*f-c*e)/(b*c-a*d);
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i]>>b[i];
	}//Read
	Circle cir; 
	cir.x=a[0];
	cir.y=b[0];
	cir.r=0;
	//Circle
	for(int i = 0;i < n;i++)	
	{
		if(cmpd(cir.r,solve2pointsdis(cir.x,cir.y,a[i],b[i]) )== -1)	
		{
			cir.x=a[i];
			cir.y=b[i];
			for(int j = 0;j < i;j++)	
			{
				if(cmpd(cir.r,solve2pointsdis(cir.x,cir.y,a[j],b[j]) )== -1)	
				{
					cir.x=solvex2points(a[i],a[j]);
					cir.y=solvey2points(b[i],b[j]);
					cir.r=(solve2pointsdis(a[i],b[i],a[j],b[j]))/2;
					for(int k = 0;k < j;k++)	
					{
						if(cmpd(cir.r,solve2pointsdis(cir.x,cir.y,a[k],b[k])) == -1){
							cir.x=solve3xpoints(a[i],b[i],a[j],b[j],a[k],b[k]);
							cir.y=solve3ypoints(a[i],b[i],a[j],b[j],a[k],b[k]);
							cir.r=solve2pointsdis(a[i],b[i],cir.x,cir.y);
						}	
					}
				}
			}
		}
	}
	printf("%.3lf %.3lf\n",cir.x,cir.y);
	printf("%.3lf",cir.r);

}

周末愉快!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值