• poj1190--生日蛋糕--经典dfs+数学剪枝


    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 = πR2
    侧面积A' = 2πRH 
    底面积A = πR2 
     
    题解:
      基于题中对于相邻两层蛋糕,下层的形状必须比上层大的要求,我们对于整个dfs过程是自下层到上层搜。
      首先无论有几层蛋糕,整个蛋糕的俯视图都是一个整圆,而这个圆的面积等于底层蛋糕的底面积。
      想要得到整个蛋糕的表面积,还需要知道每层蛋糕(圆柱体)的侧面积。侧面积的公式是 S(侧)=2*π*R*H
      题中对于体积的限制是 V=Nπ
      表面积则是 S=π*底层半径^2+每一层的侧面积
      既然π是个常数,且在每个表达式中出现,我们将每个表达式中都除以一个π,这样得到的式子中便没有了π。
      消去π后,V=N,S=底层半径^2+2*M*∑Ri*Hi(i从1~M)
      接下来是针对dfs的部分:
      在dfs前,求出每一层侧面积和体积的最小值,以便接下来的剪枝,前两个是最优性剪枝,第三个是数学剪枝
      1.如果已经用了的S+新一层最小S>已得到的ans,return
      2.如果已经用了的V+新一层最小V>N,return
      3.剩下的体积都用来做新一层的蛋糕,此时得到的新一层S+已经用了的S>ans,return
        这个剪枝可以由一个数学感知来变得显然……
        我们知道在三维空间中,对于同样体积的几个物体,越接近球体的物体的表面积越小。
        当在这道题中我们将剩下的体积全都用来做新的一层时,它的表面积最小。
        此时就变成了一个最优性数学剪枝。
     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<cmath>
     5 #include<cstring>
     6 using namespace std;
     7 int N,M;//由下层往上层搜
     8 int v[30],s[30],ans=2147483647;
     9 void dfs(int m,int used_s,int used_v,int last_r,int last_h)//下一层的半径和高度
    10 {//m当前层数,now_s已经用的面积,now_v已经用的体积
    11     if(used_v>N)return;
    12     if(m==0)
    13     {
    14         if(used_v==N)
    15             ans=min(ans,used_s);//更新答案
    16         return;
    17     }
    18     if(used_s+s[m]>ans)return;//表面积剪枝
    19     if(used_v+v[m]>N)return;//体积剪枝
    20     if(used_s+2*(N-used_v)/last_r>ans)return;//综合剪枝
    21     int max_h;
    22     for(int r=last_r-1;r>=m;r--)
    23     {
    24         if(m==M)used_s=r*r;
    25         max_h=min((N-used_v-v[m-1])/(r*r),last_h-1);
    26         for(int h=max_h;h>=m;h--)
    27         {
    28             dfs(m-1,used_s+2*r*h,used_v+r*r*h,r,h);
    29         }
    30     }
    31 }
    32 
    33 int main()
    34 {
    35     scanf("%d%d",&N,&M);
    36     for(int i=1;i<=M;i++)
    37     {
    38         s[i]=i*i*2;//侧面积
    39         s[i]+=s[i-1];
    40 
    41         v[i]=i*i*i;//体积
    42         v[i]+=v[i-1];
    43     }
    44     dfs(M,0,0,sqrt(N),N);//底层半径最多为sqrt(N)
    45     //底层高度最多为N
    46     if(ans==2147483647)
    47     {
    48         cout<<"0"<<endl;
    49         return 0;
    50     }
    51     cout<<ans<<endl;
    52     return 0;
    53 }
    View Code
  • 相关阅读:
    Git 基础笔记整理1
    学习开源中国客户端
    IOS项目中的细节处理,如更改状态栏等等
    网络编程
    当FileWriter没有flush的时候,不写入文件
    批量移动文件
    批量重命名文件
    替换一个文件内的某个字符
    遍历map和删除map中的一个entry
    ArrayList的遍历-转载
  • 原文地址:https://www.cnblogs.com/Beckinsale/p/7608887.html
Copyright © 2020-2023  润新知