poj 生日蛋糕 搜索+剪枝

生日蛋糕
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 17937   Accepted: 6384

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

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 

题意:制作一个体积为N(这里的π全部可以省略, 因为都有π,最后相当于两边都消去)的M层蛋糕。蛋糕的特点是上边的层数至少比它下面一层的半径和高度至少都少一。求最少的侧面积s(蛋糕的外表面和上层 。ps:因为上层比下层小所以外表面也包括没有被上面一层蛋糕遮住的部分,这部分投影加上表面就是下表面,所以最后的表面积就是各层蛋糕的侧面加下表面的和)
解题思路:要求出最少的表面积的和,肯定是一直枚举半径和高度,然后算出最小的表面积,这样的话肯定是深搜,这个很容易想到。但是题意中体积最大是10000, 要枚举的话肯定会用时会特别多。应该加上一点剪枝。这个题必须用好几种剪枝才能过。
       剪枝一:如果在搜索的过程中体积小于0 或者当前半径或高度小于剩余要制作的层数(因为每层必须是相差1的,当前半径或高度小于要制作的层数,肯定不能制作完)就要剪枝.
       剪枝二:当前剩下m层需要制作,可以预测制作这m层蛋糕最少需要多少体积,如果这个最少的体积还大于当前剩余的体积,那个这个不必继续搜索,所以要剪枝。
       剪枝三:因为要求求最小表面积,如果当前枚举到的面积已经大于要求得的最优表面积,就要剪枝,但是这个剪枝还可以更优化,根据剪枝二的启发,如果当前枚举到的面积加上制作剩余m层蛋糕所要花费的最小表面积大于已经求得的最优解,那么剪枝。
      剪枝四:因为体积必须为N,根据剪枝二,如果剩下m层蛋糕制作都按照最大的来,依然不能够使用完剩余的体积那么也要减掉。
根据上面四个剪枝,基本就能过这个题目.

代码:

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

int minA[30],minV[30], minArea, Area;
int N, M;

int maxvFormrh(int m, int r, int h)
{
    int maxv = 0;
    for(int i = 0; i < m; i++)
        maxv += (r-i) * (r-i) *(h-i);
    return maxv;
}

void dfs(int n, int m, int r, int h)
{
    if(m == 0)
    {
         if(n)
             return ;
         else if(minArea > Area)
         {
             minArea = Area;
             return ;
         }
    }
    if(n <= 0) // 剪枝1
        return ;
    if(h < m || r < m)//剪枝1
        return ;
    if(minV[m] > n)//剪枝2
        return ;
    if(minA[m]+Area >= minArea) // 剪枝3
        return;
    if(maxvFormrh(m,r,h) < n)//剪枝4
        return ;

    for(int i = r; i >= m; i--)
    {
          if(m == M)//最下面一层
              Area = i * i;
          for(int j = h; j >= m; j--)
          {
              Area += 2 * i * j;
              dfs(n - i*i*j, m-1, i-1, j-1);
              Area -= 2 * i * j;
          }
    }
}

int main()
{
    while(scanf("%d%d",&N,&M) != EOF)
    {
        minA[0] = 0;
        minV[0] = 0;
        for(int i = 1; i <= M; i++)//求出最小体积个最小表面积
        {
             minA[i] = minA[i-1] + 2 * i * i;
             minV[i] = minV[i-1] + i * i * i;
        }
        if(minV[M] > N)
        {
            printf("0\n");
        }
        else
        {
            int maxH = (N - minV[M-1]) / (M * M) + 1;//最大的高度
            int maxR = sqrt((N - minV[M-1]) / M) + 1;//最大的体积
            //Ps:上面的加1是因为避免出现小数取整,反正最后会减掉不必纠结。
            minArea = 1 << 30;
            Area = 0;
            dfs(N, M, maxR, maxH);
            printf("%d\n",minArea);
        }
    }
    return 0;
}




你可能感兴趣的:(搜索)