Description
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
Input
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
Output
仅一行,是一个正整数S(若无解则S = 0)。
Sample Input
100 2
Sample Output
68
Hint
圆柱公式
体积V = πR 2H
侧面积A' = 2πRH
体积V = πR 2H
侧面积A' = 2πRH
底面积A = πR 2
先找到底面最高的高度,和最大的半径,那么就需要知道最底层的最大体积,此时上面的每层肯定都是最小的,就从第一层开始每层都按最小的体积,表面积计算,每层都比上层多1,就是第一层高度1,半径1,第二层高度2,半径2等等依次
剪枝如下:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int n,m;
int mina[30],minv[30];
int area,minarea;
int maxvfornrh(int t,int r,int h)
{
int v=0;
for(int i=0; i<t; i++)
v+=(r-i)*(r-i)*(h-i);
return v;
}
void dfs(int v,int t,int r,int h) //剩余体积,层数, 最大的半径,高度
{
if(t==0)
{
if(v!=0)
return;
else
{
minarea=min(minarea,area); //比较找到最小表面积
return;
}
}
if(v<=0) //体积已用完,却没达到层数
return ;
if(minv[t]>v)//剪枝3 剩余的每层都按最小的体积计算还是大于所剩体积
return;
if(area+mina[t]>=minarea)//剪枝1 已有的表面积加上剩余的几层都按最小表面积计算还是大于已求得的最优解
return;
if(h<t||r<t) //剪枝2 现在的半径或高度小于他所在的层数,就按每层相差最小1计算,则最后半径或高度就会减到负数
return ;
if(maxvfornrh(t,r,h)<v) //剪枝4 剩下的每层都按最大的体积比所剩的体积还要小
return ;
for(int rr=r; rr>=t; rr--) //从大到小
{
if(t==m) //在最底层的时候加上底面的上表面积,即如果映射下来,全部圆柱的上表面积就可以看做最底部的上表面积,接下来只求侧面积即可
area=rr*rr;
for(int hh=h; hh>=t; hh--)
{
area+=2*rr*hh;//侧面积
dfs(v-rr*rr*hh,t-1,rr-1,hh-1);
area-=2*rr*hh;
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int maxh,maxr;
mina[0]=0;
minv[0]=0;
for(int i=1; i<=m; i++)
{
mina[i]=mina[i-1]+i*i*2; //最小表面积
minv[i]=minv[i-1]+i*i*i; //最小体积
}
if(minv[m]>n) //最小的体积比题目所给体积还要大
{
printf("0\n");
continue;
}
else
{
maxh=(n-minv[m-1])/(m*m)+1; //得到的最大高度可能是小数,不如直接多加1,最大半径也如此
maxr=sqrt((n-minv[m-1])/m)+1;
area=0;
minarea=INF;
dfs(n,m,maxr,maxh);
if(minarea!=INF)
printf("%d\n",minarea);
else
printf("0\n");
}
}
return 0;
}