参考:算法竞赛进阶指南–李煜东
搜索框架:从下往上搜索,分支则为每层的半径和高度
搜索面对的状态为:正在搜索蛋糕的第n层,当前外表面面积s,当前体积v,第n+1层的高度和半径
。可以创建数组h和r分别记录每层的高度和半径
整个蛋糕的上表面相加之和即为下表面面积,可以在第m层直接累加到s中,这样在第m-1层往上的搜
索中只用计算表面积即可,减少了计算步骤;
剪枝可以分为5个方面:
1.上下界剪枝
在第n层时,枚举r的范围[n,min(向下取整(根号N-v),r[n+1]-1)]
枚举h的范围[n,min(向下取整((N-v)/r/r),h[n+1]-1)]
这个式子可以由圆柱体积公式πRR*H=π(N-v)推导
2.优化搜索顺序
在上面确定的范围中,使用倒序枚举
3.可行性剪枝
可以预处理出从上往下前i(i>=1&&i<=n)层的最小体积和侧面积。当第i层的半径和高度分别取i时
有最小体积和最小侧面积
4.最优性剪枝一
如果当前表面积加上1~n-1 层的最小侧面积大于已经搜到的答案,放弃
5.最优性剪枝二
利用h与r数组1~n-1层的体积可表示为N-v=∑(k=1,n-1)h[k]*r[k]*r[k],
1~n-1层的表面积可表示为2∑(k=1,n-1)h[k]*r[k];
因为 2∑(k=1,n-1)h[k]*r[k]=2/r[n]*∑(k=1,n-1)h[k]*r[k]*r[n]>=2/r[n]*∑(k=1,n-1)h[k]*r[k]*r[k]>=2(N-v)/r[n] 所以当2(N-v)/r[n] +s大于已经搜到的答案时 可以剪枝
#include<bits/stdc++.h>
using namespace std;
const int inf= 0x3f3f3f3f;
int N,n,m,s=0,mins[30],minv[30],h[30],r[30],ans=inf,v=0;
void dfs(int n)
{
if(!n){//更新答案
if(v==N) ans=min(ans,s);
return ;}
for(r[n]=min((int )sqrt(N-v),r[n+1]-1);r[n]>=n;r[n]--)
for(h[n]=min((int)((double)(N-v)/r[n]/r[n]),h[n+1]-1);h[n]>=n;h[n]--)
{
if(v+minv[n-1]>N)continue;//剪枝3
if(s+mins[n-1]>ans)continue;//剪枝4
if((double)2*(N-v)/r[n]+s>ans)continue;//剪枝 5
if(n==m) s+=r[n]*r[n];
v+=r[n]*r[n]*h[n];
s+=2*r[n]*h[n];
dfs(n-1);
if(n==m)s-=r[n]*r[n];//还原现场
v-=r[n]*r[n]*h[n];
s-=2*r[n]*h[n];
}
}
int main()
{
cin>>N>>m;
mins[0]=minv[0]=0;
for(int i=1;i<=m;i++)
{
minv[i]=minv[i-1]+i*i*i;//最小体积
mins[i]=mins[i-1]+i*i;//最小侧面积 实际应为2*i*i 缩小了范围 使运算更快
}
h[m+1]=r[m+1]=inf;//
dfs(m);//倒序 剪枝 2
cout<<ans<<endl;
}