生日蛋糕
题目描述
7 月 17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为 的 层生日蛋糕,每层都是一个圆柱体。
设从下往上数第 ()层蛋糕是半径为 ,高度为 的圆柱。当 时,要求 且 。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 最小。
请编程对给出的 和 ,找出蛋糕的制作方案(适当的 和 的值),使 最小。
(除 外,以上所有数据皆为正整数)
输入格式
第一行为一个整数 (),表示待制作的蛋糕的体积为 。
第二行为 (),表示蛋糕的层数为 。
输出格式
输出一个整数 ,若无解,输出 。
样例 #1
样例输入 #1
100
2
样例输出 #1
68
题目分析
题意简述
根据蛋糕的体积与层数,求出蛋糕的制作方案使得 最小。保证 。
模型抽象
求重叠圆柱体的最小表面积。
初步思考
先思考表面积的计算方式,蛋糕表面积 = 上表面 + 侧面。其中从俯视的角度去看蛋糕,上表面实际为最底下的圆面积。而侧面积每一层都不同,计算公式为 ,体积为 。
暴力解法:
逐层遍历,把每个可能的半径与高度进行尝试。到m层时再进行打擂台。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int ans=2e9;
int r[20],h[20],n,m;
void dfs(int d,int s,int v){
//d-已处理层数 s-当前s 当前v
if(d==m){//m层处理完
if(v==n){//满足体积为n
ans=min(ans,s);//打擂台找最小s
}
return ;
}
//枚举所有的半径与高度的组合
for(int i=r[d]-1;i>=1;i--){
for(int j=h[d]-1;j>=1;j--){
r[d+1]=i;
h[d+1]=j;
if(d==0)//第一层时,额外加上上表面
dfs(d+1,s+i*i+2*i*j,v+i*i*j);
else
dfs(d+1,s+2*i*j,v+i*i*j);
}
}
}
int main(){
cin>>n>>m;
r[0]=sqrt(n);
h[0]=sqrt(n);
dfs(0,0,0);
if(ans==2e9) cout<<0;
else cout<<ans;
return 0;
}
优化方向:
当前搜索时,每一层的半径、高度组合过多,可以尝试进行剪枝。
优化思考
当前存在两个限制条件:1. 表面积 2. 体积。若搜索过程中体积已超出限定的n则不用继续搜索。整体寻找的最小的表面积,当前若 ,则越往后只会越来越大,也不需要继续。
void dfs(int d,int s,int v){
//d-已处理层数 s-当前s 当前v
if(s>=ans) return ;//最优性剪枝
if(v>n) return ;//可行性剪枝
...
}
当前的剪枝还不够,还需要考虑其他方向。对于体积与侧面积的计算公式为 。当前剩余体积为 ,高度设 ,对应的表面积为 。若预测出的表面积已经超过了已存储的最小表面积,则不需要继续枚举对应的半径、高度组合了。
void dfs(int d,int s,int v){
//d-已处理层数 s-当前s 当前v
if(s>=ans) return ;//最优性剪枝
if(v>n) return ;//可行性剪枝
if((n-v)/r[d]*2+s>ans) return;//剩余体积构成的面积,超过了记录的最小值
...
}
解题流程
- 步骤1:逐层枚举所有可能的半径、高度组合,直到处理到第m层。
- 步骤2:加入最优性剪枝、可行性剪枝。
- 步骤3:预测剩余体积构成的面积,进行剪枝。
代码实现
#include<bits/stdc++.h>
using namespace std;
int ans=2e9;
int n,m;
int r[20],h[20];
void dfs(int d,int s,int v){
//d-已经确定好的层数,s-当前的面积
if(s>=ans) return;//最优性剪枝
if(v>n) return;//可行性剪枝
if((n-v)/r[d]*2+s>ans) return;//剩余体积构成的面积,超过了记录的最小值
if(d==m){
if(v==n){
ans=s;
}
return;
}
//缩小范围
for(int i=r[d]-1;i>=m-d;i--){
for(int j=h[d]-1;j>=m-d;j--){
r[d+1]=i;
h[d+1]=j;
if(d==0) dfs(d+1,s+i*i+2*i*j,v+i*i*j);
else dfs(d+1,s+2*i*j,v+i*i*j);
}
}
}
int main(){
cin>>n>>m;
r[0]=sqrt(n);
h[0]=n;
dfs(0,0,0);
if(ans==2e9) cout<<0;
else cout<<ans;
return 0;
}
总结回顾
本题的关键在于剪枝的处理:
- 当前体积超过给定体积,则结束。
- 当前表面积已记录的最小表面积。
- 估算出剩余体积构成的表面积,利用预估结果进行剪枝。