蓝桥杯ALGO-177 balloon in box (100%测试数据通过)

本文解决了一个计算几何问题:如何在长方体内放置多个球形气球以占据最大体积。通过全排列枚举不同放置顺序,并计算每个球的最大半径。

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

问题描述:

  你要写一个程序,使得能够模拟在长方体的盒子里放置球形的气球。
  接下来是模拟的方案。假设你已知一个长方体的盒子和一个点集。每一个点代表一个可以放置气球的位置。在一个点上放置一个气球,就是以这个点为球心,然后让这个球膨胀,直到触及盒子的边缘或者一个之前已经被放置好的气球。你不能使用一个在盒子外面或者在一个之前已经放置好的气球里面的点。但是,你可以按你喜欢的任意顺序使用这些点,而且你不需要每个点都用。你的目标是按照某种顺序在盒子里放置气球,使得气球占据的总体积最大。
  你要做的是计算盒子里没被气球占据的体积。

输入格式

  第一行包含一个整数n表示集合里点的个数(1≤n≤6)。第二行包含三个整数表示盒子的一个角落的(x,y,z)坐标,第三行包含与之相对的那个角落的(x,y,z)坐标。接下来n行,每行包含三个整数,表示集合中每个点的(x,y,z)坐标。这个盒子的每维的长度都是非零的,而且它的边与坐标轴平行。

输出格式

  只有一行,为那个盒子没被气球占据的最小体积(四舍五入到整数)。

样例输入

2
0 0 0
10 10 10
3 3 3


7 7 7

样例输出

774

数据规模和约定

  所有坐标的绝对值小于等于1000
  对于20%的数据:n=1
  对于50%的数据:1≤n≤3
  对于100%的数据:1≤n≤6

 

问题分析:首先,题目的大致要求就是在一个长方体内放置一定数量的气球,用一定的放置方式使得长方体内剩余体积最小,即求每一个气球的最大半径。由于题目给出了所有气球的球心坐标的我们很容易能想到用全排列的方式枚举所有情况。

涉及知识:计算几何 枚举

算法难点:求出每个球的最大半径(连心球)

代码:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
const double pi = acos(-1.0);

typedef struct vp
{
	double x,y,z;
	double r;
}P;
P vp[20],s,e;
int n;
double l,w,h;


double minvd(double x,double y,double z)//圆心到长方体的最小长度
{
	double t1 = min(fabs(x-s.x),fabs(x-e.x));
	double t2 = min(fabs(y-s.y),fabs(y-e.y));
	double t3 = min(fabs(z-s.z),fabs(z-e.z));
	double m = min(t1,t2);
	return min(m,t3);
}
double dis(P p1,P p2)//两点间的距离
{
	double x = fabs(p1.x-p2.x);
	double y = fabs(p1.y-p2.y);
	double z = fabs(p1.z-p2.z);
	return sqrt(x*x+y*y+z*z);
}
bool out(double x,double y,double z)//判断点是否在长方体外
{
	if(fabs(x-s.x)>w||fabs(x-e.x)>w)
		return true;
	if(fabs(y-s.y)>l||fabs(y-e.y)>l)
		return true;
	if(fabs(z-s.z)>h||fabs(z-e.z)>h)
		return true;
	return false;
}

int main(void)
{
	int i,j,k,a[10];
	while(cin>>n&&n>0)
	{
		cin>>s.x>>s.y>>s.z;
		cin>>e.x>>e.y>>e.z;
	    w = fabs(s.x-e.x),l = fabs(s.y-e.y),h = fabs(s.z-e.z);
		double sum = l*w*h,maxx=0.0;

		for(i=0;i<n;i++)
		{
			cin>>vp[i].x>>vp[i].y>>vp[i].z;
			a[i] = i;
		}

		double temp,mr;
		do{
			temp = 0;
			for(i=0;i<n;i++)//对于每一种排列的每个气球
			{
				if(out(vp[a[i]].x,vp[a[i]].y,vp[a[i]].z))
					continue;

				vp[a[i]].r = minvd(vp[a[i]].x,vp[a[i]].y,vp[a[i]].z);//确定第一个圆的半径
				for(j=0;j<i;j++)//与它前面的气球对比,求半径
				{

					if(out(vp[a[i]].x,vp[a[i]].y,vp[a[i]].z))
						continue;
					 double vd = dis(vp[a[i]],vp[a[j]])-vp[a[j]].r;
					 if(vd<0)vd=0;
					 vp[a[i]].r = min(vp[a[i]].r,vd);
				}
				temp+=4.0/3.0*pi*vp[a[i]].r*vp[a[i]].r*vp[a[i]].r;
			}
			maxx = max(maxx,temp);

		}while(next_permutation(a,a+n));//an的全排列
		printf("%.0lf\n",sum-maxx);
	}
	return 0;
}

总结:第一次做这种稍微有些复杂的计算几何题,虽然很快想到了用next全排列,但由于对数学公式不熟悉在求半径上卡了很久。直到参考了另一位博主的代码才想到连心球求半径的方法https://www.cnblogs.com/ruruozhenhao/p/8428458.html。(PS:不知道什么原因,他的代码只通过了50%的测试数据)。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值