题解 P1731 【生日蛋糕】
Undefined_Myth · · 题解
#include<cstdio>
#include<cstdlib>
using namespace std;
#define MAXN 20005
#define MAXM 16
int n,m;
int Vmin[MAXM+1];
int ans=999999999;//记得初始化一个特别大的值
void dfs(int dep,int r,int h,int s,int v)//还剩下x层,之前一层半径r,高度h,s 为到了这里时的表面积 v 为剩余的体积
{
//剪枝
if(Vmin[dep] > v) return; //最小都不能构造完 cut
// r h 为之前的数据 r-1 h-1 是现在的最大值
if(dep*(r-1)*(r-1)*(h-1) < v) return;//如果剩余的体积太大的话,连之后的每层的体积较这一层都没有少都不能用完则 cut
//本来剪枝要更细的 但是由于复杂度高 而且难算难想 所以我们把界限可以放宽一点
//v == 0其实可以不加 因为当 dep = 0 则Vmin[dep] = 0 dep*(r-1)*(r-1)*(h-1) = 0 那么v >(或者<0) 0 不满足 所以v = 0
if(dep == 0 && v == 0){
if(s < ans) ans = s;
return ;
}
//枚举r 和 h
/*for(int i=dep;i*i*dep<=v;i++){
if(s + 2 * v / i > ans) continue;
for(int j=dep;i*i*j<=v;j++)
dfs(dep-1,i,j,s+2*i*j,v-i*i*j);
}*/
//枚举当前一层的半径
for(int i=dep;i<r;i++){//不写上面的那种 是因为 <r <h 更优,而且不满足的话 在上面的if一定会被cut v 会变负数
if(s + 2 * v / i > ans) continue; //可行性剪枝
for(int j=dep;j<h;j++) //枚举当前一层的高度
dfs(dep-1,i,j,s+2*i*j,v-i*i*j);
}
}
/* //注意下面的字母有些地方时有下划线的
怎样求剩余部分侧面积下限?
V=Ri+12*Hi+1+Ri+22*Hi+2+…+Rm2*Hm
2V=Ri+1*Si+1+Ri+2*Si+2+…+Rm*Sm
2V<Ri+1*S总min
即S总min>2V/Ri+1
*/
int main()
{
scanf("%d%d",&n,&m);
//枚举底层的r和h
//记得初始化Vmin数组
Vmin[0] = 0;
for(int i=1;i<=m;i++)//Vmin是这一层最小的体积 1~m 是从上到下
Vmin[i]=Vmin[i-1]+i*i*i;
//i*i*m == n 是可以的 即只有一层
for(int i=m;i*i*m<=n;i++)//i*i*m<n 代表的是如果最底下那层的体积都>n 那么肯定不用考虑了 其他的情况后面有考虑和剪枝
for(int j=m;i*i*j<=n;j++)
dfs(m-1,i,j,i*i+2*i*j,n-i*i*j);//注意到这里已经用了一层了 所以是m-1
//特别注意: 由于是总共的表面积,那么除了侧面积之外还有每一层的上表面 把它们平移往上则是底层的底面积 而底层是不用涂的
//所以只需要 也必须要+i*i
//由于圆周率是相同的 所以同时约去是没有影响的
printf("%d",ans);
return 0;
}