这道题目我们使用深搜加剪枝的方法来写,我们首先算出一个最小表面积和最小体积来,就是半径从一递增,高度也从一递增,这是题目要求。
然后我们计算出一个底层最大的半径和最大的高度,我们就从这个最大半径和最大高度开始深度优先搜索,每一层的蛋糕都从最大半径开始深搜,然后内循环里面同样是对应每一层不同半径不同高度的深搜,所以就是双重循环进行搜索。
但是在搜索之前我们要进行剪枝,第一个剪枝是在边界条件里面,如果层数等于零了,但是v还不等与零,我们就直接返回,如果体积等于零了说明此方案可行,然后把它和最小表面积进行比较并赋值。
或者层数还不等于零,但是此时的体积已经小于零了,我们也直接返回。
其余的剪枝都是在for循环里面进行的,我们首先进行最优性剪枝,并且预测一下如果之前的表面积加上这层的表面积再加上之后最小的层数对应的表面积,如果此时大于了已经求得的最优表面积,我们就直接返回。
与此对应的可行性剪枝就是,如果总体积减去之前的体积,减去这层的体积,再减去之后层数的最小体积小于零的话,我们也直接返回。
如果,这层的最大半径减去层数加一小于零了,或最大高度减去层数加一小于零了,我们也直接返回,因为每层的半径和高度都要大于上一层,所以最小的公差就是一,如果最宽泛的这个顶层范围都无法满足的时候,我们就不必再搜了。
最后一个剪枝就是假设之后的每一层蛋糕都按照最大的半径和高度建造,如果这时的体积依旧要小于我们要建的体积,我们也直接返回了。
因为这个深搜的范围很宽,而且题目当中的时间限制是一秒,所以我们剪枝剪得精细一些才能过。
代码如下:
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
int N,M;
int minArea=1<<30;
int area=0;
int minV[25];//k层及之上的蛋糕最小总体积
int minA[25];//k层及之上的蛋糕的最小表面积
void Dfs(int v,int n,int max_r,int max_h)
{
if (n==0) {
if (v!=0) {
return ;
}
else {
minArea=min(minArea,area);
return ;
}
}
if (v<0)
return ;
for (int r=max_r;r>=n;r--) {
if (n==M)
area=r*r;
for (int h=max_h;h>=n;h--) {
if (area+2*r*h>=minArea)
continue;
if (v-r*r*h-minV[n-1]<0)
continue;
if (max_r-n+1<=0||max_h-n+1<=0)
return ;
int sum=0;
for (int i=n-1;i>=0;i--) {
sum+=(r-i)*(r-i)*(h-i);
}
if (sum<v)
continue;
area+=2*r*h;
Dfs(v-r*r*h,n-1,r-1,h-1);
area-=2*r*h;
}
}
}
int main()
{
cin>>N>>M;
int maxH,maxR;
memset(minV,0,sizeof(minV));
memset(minA,0,sizeof(minA));
for (int i=1;i<=20;i++) {
minA[i]=2*i*i+minA[i-1];
}//侧面积累加
for (int i=1;i<=20;i++) {
minV[i]=minV[i-1]+i*i*i;//最小体积累加
minA[i]+=i*i;//最小表面积累加
}
maxR=sqrt(N-minV[M-1]);
maxH=N-minV[M-1];
Dfs(N,M,maxR,maxH);
if (minArea==1<<30)
cout<<0<<endl;
else
cout<<minArea<<endl;
return 0;
}