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

 

posted @ 2017-09-28 22:07  BK-Edwina  阅读(448)  评论(0编辑  收藏  举报