题目大意:制作一个每层都是圆柱体,体积Nπ的M层蛋糕,下层的比上层的半径更大,高度更高,找出每层适当的Ri和Hi使得蛋糕表面积(最下一层的下底面除外)Sπ最小,求S的最小值,以上数据皆为正整数
输入:N(N <= 10000)
M(M <= 20)
输出:S(若无解输出0)
分析:dfs+花式大剪枝
剪枝方法如下:
1.半径r,与高h都从N+1开始搜索。
2.因为前level层的面积为s,如果剩下的几层的面积都取最小可能值,所得的面积和比已经得到的所求的最小面积best大,也可以进行剪枝(剪枝条件为:s+mins[dep-1]>best)
3.因为前level层的体积为v,如果剩下的几层的体积都取最小可能值,总体积还是比n大,那么则说明前level层的方案不可行,所以可以剪枝(剪枝条件为:v+minv[dep-1]>n)
4.(目前体积-已有体积)/r*2+已有的表面积若大于已经得到过的S则减掉。
/**
*2*r*h=S
* r*r*h=V
* => V*2/r = S
* S + sumS >= best => return;
*/如果把剩下的所有体积,全部用来只做成一个圆柱体,这种情况加所增加到表面积是最小的。
代码:转载自http://blog.csdn.net/v5zsq/article/details/46575215
#include<stdio.h>
#define in(a,b) (a<b?a:b)
int n,m;
int minv[21],mins[21];
int bests;
void dfs(int v,int s,int level,int r,int h)//v为体积,s为表面积,level为搜索深度,从底层m层向上搜,r,h分别为level的上一层的半径和高度
{
if(level==0)//搜索完成,则更新最小面积值
{
if(v==n&&s<bests)
bests=s;
return ;
}
if(v+minv[level-1]>n||s+mins[level-1]>bests||2*(n-v)/r+s>=bests)//剪枝
return ;
int i,j,hh;
for(i=r-1;i>=level;i--)//按递减顺序枚举level层蛋糕半径的每一个可能值,这里第level层的半径最小值为level
{
if(level==m)//底面积作为外表面积的初始值(总的上表面积,以后只需计算侧面积)
s=i*i;
hh=in((n-v-minv[level-1])/(i*i),h-1); //最大高度,即level层蛋糕高度的上限,(n-v-minv[level-1])表示第level层最大的体积
for(j=hh;j>=level;j--)//同理,第level层的最小高度值为level
dfs(v+i*i*j,s+2*i*j,level-1,i,j);//递归搜索子状态
}
}
int main()
{
int i;
minv[0]=0;
mins[0]=0;
for(i=1;i<=20;i++)//从顶层向下计算出最小体积和表面积的可能值
{
//从顶层(即第一层)到第i层的最小体积minv[i]成立时第j层的半径和高度都是j
minv[i]=minv[i-1]+i*i*i;
mins[i]=mins[i-1]+2*i*i;
}
while(scanf("%d%d",&n,&m)==2)
{
bests=0x7fffffff;
dfs(0,0,m,n+1,n+1);
if(bests==0x7fffffff)
printf("0\n");
else
printf("%d\n",bests);
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48