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外,以上所有数据皆为正整数)
设从下往上数第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 = πR2H
侧面积A' = 2πRH
底面积A = πR2
体积V = πR2H
侧面积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 }