其他分享
首页 > 其他分享> > 生日蛋糕 剪枝

生日蛋糕 剪枝

作者:互联网

生日蛋糕

7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为 Nπ 的 M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第i层蛋糕是半径为Ri, 高度为 Hi 的圆柱。

当 i < M 时,要求Ri > Ri+1且Hi > Hi+1。

由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。

令Q = Sπ ,请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。

除Q外,以上所有数据皆为正整数 。

输入格式

输入包含两行,第一行为整数N(N <= 10000),表示待制作的蛋糕的体积为NπNπ。

第二行为整数M(M <= 20),表示蛋糕的层数为M。

输出格式

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

数据范围

1≤N≤10000
1≤M≤20

输入样例:

100
2

输出样例:

68



题意:

    体积固定为 n π 、固定m层,每一层都是一个圆柱体,
  从上往下,圆柱的半径、高度二者必须都是递减的,
  也就是 r[i+1]>r[i] 、 h[i+1]>h[i]
  找出一个方案使得整个圆柱的外表面积最小(除了最底下那一层的下低面)
  最后的答案除π

题解:
  

   搜索包含的状态:当前的层数,已经确定的体积,已经确定的面积

  题目中对最后的答案除圆周率,所以可以直接忽略

 

 

 

  1)优化搜索顺序

    蛋糕从半径和高度都大的底层开始搜索,能够减少后面的分支个数

  2)上下界剪枝

    ·当前最小的半径可能的两种

       ·上一层的半径 - 1

       ·总体积-已经确定的体积 sqrt( N - v )

       取二者最小值作为枚举的首项从大到小枚举

     当前最小的高度可能的两种

       ·上一层的高度-1

       ·( N - v)/ r^2 

     因为h中可能的最大值中一个取决于 r 的大小,所以先枚举 r ,同样取二者最小值作为枚举的首项从大到小枚举

    可以得到当前半径R的范围和高度H的范围

      ·R -> [ deep , min ( | sqrt( N - v |  ,  R[deep+1]-1 ) ]

      ·H -> [ deep , min (  N - v)/ r^2  ,  H[deep+1]-1 ) ] 

  3)可行性剪枝
    预处理从上往下,第i层最小的体积和侧面积,1~i层最小的高度和半径都是 1,2,3,...... ,i
    所以可以求的第 i 层的最小的体积是 i*i*i,最小面积是2*i*i
    如果当前的体积加上前i层的最小体积都 > N 直接回溯

  4)最优性剪枝
    ·如果当前层 i 的表面积加上前面已经得到的最小侧面积大于搜索到的答案可以直接剪枝
      
    ·1~u层的体积可以表示为 n-v = ∑i=[1,u](H[i]*R[i]^2)
     1~u层的面积可以表示为
    
2* ∑i=[1,u](R[i]*H[i])= 2/R[u+1] *∑i=[1,u](R[i]*H[i]*R[u+1])>=2/R[u+1] *∑i=[1,u](R[i]*H[i]*R[u+1])
    >=2*(n-v)/R[u+1]
    因为n-v可以求得,所以放缩成可以用n-v表示的不等式
    当
2*(n-v)/R[u+1]+s的表面积大于已搜到的答案的时候剪枝
    
    以上两个剪枝保证了搜到底符合条件的答案一定是可以去更新当前答案的所以一定是最优解
代码:
  
  1. #include<bits/stdc++.h>  
  2. using namespace std;  
  3. const int N=25;  
  4. int n,m;  
  5. int R[N],H[N];  
  6. int minv[N],mins[N];  
  7. int ans=INT_MAX;  
  8. void dfs(int u,int v,int s){  
  9.     if(v+minv[u] > n) return;  
  10.     if(s+mins[u] >= ans) return;  
  11.     if(2*(n-v)/R[u+1]+s>=ans) return;//关键最优性剪枝  
  12.    
  13.     if(!u){  
  14.         if(v==n) ans=s;//剪枝将所有大的都剪掉了剩下的一定是能够更新最小值的  
  15.         return;  
  16.     }  
  17.     for(int r = min( R[u+1]-1,(int)sqrt(n-v)); r >= u; r --)//优化搜索顺序,先从大的r开始,h由r确定最大  
  18.         for(int h = min( H[u+1] - 1, (n - v) / r / r); h >= u; h--){  
  19.             int t=0;  
  20.             if(u==m)  
  21.                 t=r*r;  
  22.             R[u]=r;  
  23.             H[u]=h;  
  24.             dfs(u-1,v+r*r*h,s+t+2*r*h);  
  25.         }  
  26. }  
  27. int main(){  
  28.     cin>>n>>m;  
  29.    
  30.     for(int i=1;i<=m;i++){  
  31.         minv[i]=i*i*i;  
  32.         mins[i]=2*i*i;  
  33.     }  
  34.     R[m+1]=H[m+1]=INT_MAX;  
  35.    
  36.     dfs(m,0,0);  
  37.     cout<<ans<<endl;  
  38. }  
 

标签:剪枝,int,最小,半径,枚举,体积,生日蛋糕
来源: https://www.cnblogs.com/hhyx/p/12357926.html