#10019. 「一本通 1.3 例 2」生日蛋糕

【题目描述】

Mr.W 要制作一个体积为 Nπ 的 M 层生日蛋糕,每层都是一个圆柱体。 设从下往上数第 i 蛋糕是半径为 Ri​,高度为 Hi​ 的圆柱。当 iRi+1​且 Hi​>Hi+1​。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q最小。 令 Q =Sπ ,请编程对给出的 N 和 M ,找出蛋糕的制作方案(适当的 Ri​ 和 Hi​ 的值),使 S 最小。

(除 Q 外,以上所有数据皆为正整数)

#10019. 「一本通 1.3 例 2」生日蛋糕_第1张图片

【输入格式】

第一行为 N ,表示待制作的蛋糕的体积为 Nπ;

第二行为 M ,表示蛋糕的层数为 M 。

【输出格式】

输出仅一行,一个整数 S(若无解则 S=0 )。

【样例输入】

100
2

【样例输出】

68
【附:圆柱相关公式:】

体积 V= π R 2 H \pi R^{2}H πR2H

侧面积 S ′ = 2 π R h S{}'=2\pi Rh S=2πRh

底面积 S = π R 2 S=\pi R^{2} S=πR2

【数据范围与提示】

对于全部数据,1≤N≤10^4,1≤M≤20。

大致思路

  • 首先,我们要找出在每一层最大的体积与面积为多少,方便我们来进行优化

ss[0]=0;vv[0]=0;
	for(int i=1;i<=17;i++){
		ss[i]=ss[i-1]+2*i*i;//面积
		vv[i]=vv[i-1]+i*i*i;//体积
	}
  • 根据题意,在dfs中为五个变量, v为已用体积,s为已有表面积,p为剩余层数,r为半径,h为高
inline void dfs(int v,int s,int m,int r,int h){
  • dfs如何实现?我们枚举半径r和高h
for(int i=r-1;i>=m;i--){//枚举上一层的半径
		if(m==mm)s=i*i;
		for(int j=min((n-v-vv[m-1])/(i*i),h-1);j>=m;j--){//枚举上一层的高
			dfs(v+i*i*j,s+2*i*j,m-1,i,j);
		}
  • 正常的dfs是会超时的,考虑剪枝:
  • 若体积超限,return
  • 若表面积大于当前ans,return
  • 若当前表面积加上余下的侧面积大于ans,则此情况一定不可能优于当前答案,直接return
if(2*(n-v)/r+s>ans)return;//最重要的剪枝:当前的表面积+余下的侧面积>当前最优值
	if(v+vv[m]>n)return;//体积超出 
	if(s+ss[m]>ans)return;//表面积超出

剪完枝就可以完美ac啦

#include
using namespace std;
int n,mm,ans=1e9;
int ss[130],vv[130];//ss存储表面积,vv存储体积 
inline void dfs(int v,int s,int m,int r,int h){
	if(2*(n-v)/r+s>ans)return;//最重要的剪枝:当前的表面积+余下的侧面积>当前最优值
	if(v+vv[m]>n)return;//体积超出 
	if(s+ss[m]>ans)return;//表面积超出
	if(m==0){//v为已用体积,s为已有表面积,p为剩余层数,r为半径,h为高 
		if(v==n)ans=min(s,ans);//判断是否符合要求并得到更优解
		return;
	}	
	
	for(int i=r-1;i>=m;i--){//枚举上一层的半径
		if(m==mm)s=i*i;
		for(int j=min((n-v-vv[m-1])/(i*i),h-1);j>=m;j--){//枚举上一层的高
			dfs(v+i*i*j,s+2*i*j,m-1,i,j);
		}
	}
}
int main(){
	cin>>n>>mm;
	ss[0]=0;vv[0]=0;
	for(int i=1;i<=17;i++){
		ss[i]=ss[i-1]+2*i*i;//第i层使用的最大表面积 
		vv[i]=vv[i-1]+i*i*i;//第i层使用的最大体积 
	}
	dfs(0,0,mm,sqrt(n),sqrt(n));//r,h最大值为sqrt(n) 
	if(ans==1e9)cout<<0;
	else cout<<ans;
	return 0;
}

额外注:若剪枝放在答案判断后面洛谷的加强版数据会re(会栈溢出,虽然我不知道是为什么

你可能感兴趣的:(深度优先,算法,图论)