vijos 生日蛋糕

来源:互联网 发布:淘宝同城交易怎么弄 编辑:程序博客网 时间:2024/05/02 00:15

描述

7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为N*pi的M层生日蛋糕,每层都是一个圆柱体。 设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。 由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。 令Q = S*pi 请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。(除Q外,以上所有数据皆为正整数)

格式

输入格式
每组数据两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为N*pi;第二行为M(M <= 20),表示蛋糕的层数为M。

输出格式
仅一行,是一个正整数S(若无解则S=0)。
样例1

样例输入1
100
2

样例输出1
68

来源
NOIP99


//生日蛋糕-Milky#include<cstdio>#include<cmath>using namespace std;int V,N,vmin[10005],smin[10005],ans=0x7fffffff;void dfs(int i,int r,int h,int snow,int vnow){    for(int ri=r-1; ri>=N-i+1; --ri)        for(int hi=N-i+1; hi<h; ++hi)        {            int s0=2*ri*hi,v0=ri*ri*hi;            if(i==1) snow=ri*ri;            if(snow+s0+smin[i+1]<ans&&vnow+v0+vmin[i+1]<=V&&vnow+v0+(N-i)*v0>=V)            {                if(i==N) ans=snow+s0;                else dfs(i+1,ri,hi,snow+s0,vnow+v0);            }        }}int main(){    scanf("%d%d",&V,&N);    for(int i=1;i<=N;++i)    {        vmin[N-i+1] = vmin[N-i+2] + i*i*i;        smin[N-i+1] = smin[N-i+2] + 2*i*i;    }    int max_r1=sqrt((V-vmin[2])/N);    int max_h1=(V-vmin[2])/(N*N);    dfs(1,max_r1+1,max_h1+1,0,0);    if(ans==0x7fffffff) ans=0;    printf("%d\n",ans);    return 0;}

代码很短,思路能看出来。

为什么半径倒搜,高度正搜呢?
由于除了最底层以外,每一层蛋糕的表面积都是侧面积,求侧面积的公式是 2*r*h。
我们再回到代码14行,s0=2*ri*hi,v0=ri*ri*hi
在 s 中,r 和 h 的增长都是线性的;v 中,r 的增长是平方的,h 相应的减小也是平方的。因此 r 越大,h 越以平方性减小,所以在相同的 v 下,得到的 s 就越小。
所以半径倒搜,高度正搜,可以更快地找到最优解。在此之后的搜索都会被剪枝,效率更高。

题解中还有一个思路,加上以后剪枝更严格,效率更高,
上面已经证明,当体积一定时,半径越大,侧面积越小。假设剩余的蛋糕层都和当前层一样,r 都为当前 r,这样 r 就是最大的了,如果计算所得侧面积与当前面积之和大于等于最优解,退出。