Description
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
Input
Output
Sample Input
100 2
Sample Output
68
Hint
圆柱公式体积V = πR 2H
侧面积A' = 2πRH
底面积A = πR2
解析:我们在计算过程中根本就没必要去带入π值,其次这个题的一大重点在于剪枝,我们可以得到三种剪枝方法,在代码中会解释
代码: #include<stdio.h>
#define INF 10000000
#define min(a,b) (a<b?a:b)
int n,m,best,minv[22],mins[22];
void Init()
{
int i;
minv[0]=0;
mins[0]=0;
for(i=1;i<22;i++)//从顶层向下计算出最小体积和表面积的可能值
{
//从顶层(即第1层)到第i层的最小体积minv[i]成立时第j层的半径和高度都为i
minv[i]=minv[i-1]+i*i*i;
mins[i]=mins[i-1]+2*i*i;
}
}
//dep:搜索深度,从底层m层向上搜,r,h分别为该层的半径和高度
void DFS(int dep,int sumv,int sums,int r,int h)
{
int i,j,maxh;
if(dep==0)//搜索完成,则更新最小面积值
{
if(sumv==n&&sums<best) //当涂的体积和要求体积相等时,比较当前涂过的面积和最优解
best=sums;
return;
}
//剪枝如上面所述
if((sumv+minv[dep-1]>n//如果当前涂过的体积和我们在后面能够涂的最小体积之和都大于题目的要求体积,那么肯定不行)||(sums+mins[dep-1]>best//如果当前涂过的面积和我们在后面能够涂的最小面积之和都大于最优面积,那么肯定不行)||(2*(n-sumv)/r+sums>=best)) //我们涂过sums的面积,那么剩下的rest_s等于sum(2*Ri*Hi,一直到最后一层),肯定大于sum(2*Ri*Ri*Hi/r)(r为当前层的半径,由于我们是从下往上递推,那么Ri/r肯定小于1,因为从当前层往上推,半径在逐渐减小,因为sum(2*Ri*Ri*Hi)=2*(N-v)/r,sum(Ri*Ri*Hi)为后面几层的体积之和,并且2*(N-v)/r为剩下的几层的总侧面积)
return;
//按递减顺序枚举dep层蛋糕半径的每一个可能值,这里第dep层的半径最小值为dep
for(i=r-1;i>=dep;i--)
{
if(dep==m)//底面积作为外表面积的初始值(总的上表面积,以后只需计算侧面积) 因为上层的底面积被它的下层所掩盖,我们加了之后,就不需要算它的顶部圆面积了,每次的dfs都会被计算在内了
sums=i*i;
//最大高度,即dep层蛋糕高度的上限,(n-sumv-minv[dep-1])表示第dep层最大的体积
maxh=min((n-sumv-minv[dep-1])/(i*i),h-1);
for(j=maxh;j>=dep;j--)//同理,第dep层的最小高度值为dep
DFS(dep-1,sumv+j*i*i,sums+2*i*j,i,j);//递归搜索子状态
}
}
int main()
{
Init();
while(~scanf("%d%d",&n,&m))
{
best=INF;
DFS(m,0,0,n+1,n+1);
if(best==INF)
best=0;
printf("%d\n",best);
}
return 0;
}