生日蛋糕(dfs+剪枝)

生日蛋糕

 POJ - 1190 

题目:

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外,以上所有数据皆为正整数) 

Input

有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。

Output

仅一行,是一个正整数S(若无解则S = 0)。

Sample Input

100

2

Sample Output

68

Hint

圆柱公式 
体积V = πR 2
侧面积A' = 2πRH 
底面积A = πR 2 

思路:

         很容易就可以知道是按照层数进行dfs。当满足层数条件以及体积条件时,找到最小的答案就可以输出了。只是为什么想把这题目写出来呢?因为这题目对时间的要求看似宽松,实则让人想吐血。写出来的dfs需要大量地剪枝,尤其是hnust的oj上对剪枝的要求简直是……太严格了。主要是需要剪枝的有三大类:

1)  n+minv[m-1]>N 当前已经计算出来的蛋糕的体积,加上剩下的层数中可能制造出的最少的蛋糕的体积的结果如果大于n,剪掉。

2)  sum+mins[m-1]>ans 当前已经计算出来的蛋糕的表面积,加上剩下层数中可能制造出的最少的蛋糕的表面积的结果如果大于之前计算出来的结果(ans)时,剪掉。

3)  sum+2*(N-n)/r>=ans 数学公式推导:n-sumv既所剩体积记作dv 还需要的表面积为s

         s=2*ri*hi+2*r(i-1)*h(i-1)+... >=2*ri*hi*ri/r+2*r(i-1)*h(i-1)*r(i-1)/r+...

                             =2*dv/r(i从M-1取,r为当前半径 ri/r<1)

     所以得到还需要的最小表面积s=2*(n-sum)/r,如果最小的s和已经搜索过的表面积sum依然比ans大 就不用继续搜索了

如果还是时间超限的话(例如hnust里提供的数据)就需要第四次剪枝了。

4)当前计算出来的体积,加上可能的最大的蛋糕体积的结果小于n,剪掉。这个是参考了大佬的思路,就不贴出来了,都找得到

AC代码:

#include<bits/stdc++.h>

using namespace std;
int h,r,N,M,mins[25],minv[25];
const int inf=1<<30;
int ans=inf;
int sum=0;
void dfs(int r,int h,int n,int m,int sum)
{
    if(m==0)
    {
        if(n==N&&sum<ans)
        {
            ans=sum;//cout<<r<<" "<<h<<endl;
        }
        return;
    }
    if(n+minv[m-1]>N||sum+mins[m-1]>ans||sum+2*(N-n)/r>=ans)
        return;
    for(int i=r-1; i>=m; i--)
    {
        int jl=i*i;
        if(m==M)
            sum=jl;
        int maxh=((N-n-minv[m-1])/(i*i)<h-1)? (N-n-minv[m-1])/(i*i):h-1;
        for(int j=maxh; j>=m; j--)
        {
            dfs(i,j,n+jl*j,m-1,sum+2*i*j);
        }
    }
}
int main()
{
    minv[0]=0;
    mins[0]=0;
    for(int 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)
    {
        ans=inf;
        int rmax=(int)sqrt((double)N); //rmax初始半径 底层半径 最大值为sqrt(n)
        int hmax=N;                    //hmax初始高度 高度最大为 n
        dfs(rmax,hmax,0,M,0);
        if(ans==inf)
            cout<<0<<endl;
        else
            cout<<ans<<endl;
    }
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/lavena/p/11231974.html
今日推荐