POJ 6048 泰国佛塔 【dfs搜索】【暴力大比拼】【北大ACM/ICPC竞赛训练】

 反正想清楚了就开始枚举吧,需要拿纸和笔推一些公式。

我们想要m层的蛋糕有体积为N,那就枚举每一层蛋糕的半径和高度。其中第i层的蛋糕要比第i-1层的蛋糕半径和高度都大,第一层的蛋糕最小。

发现dfs中要带四个参数,dfs(level,remainVolumn,maxRadius,maxHeight)代表用level层蛋糕要用掉remainVolumn这么多体积,其中第level层蛋糕最大半径是maxRadius,最高是maxHeight

那怎么确定枚举的【半径和高度】的范围呢,我们考虑第m层(最底层)蛋糕的最大半径和最高高度。

半径最大时是第m层用尽可能大的体积【第m-1至第1层的蛋糕都尽可能小==>第1层r=1,h=1;第2层r=2,h=2;第i层r=i,h=i】且高度尽可能小(也就是m)

高度最高是用尽可能大的体积且半径尽可能小(也就是m)

实现时我们可以处理出数组volumn[i]代表第i层到第1层所用最小体积

 

搜索的话我们要尽可能剪枝,不然它指数级复杂度就TLE了。剪枝大体分为【可行性剪枝】和【最优性剪枝】,对于这道题来说可以有很多种剪枝姿势。

1.剩下的蛋糕尽可能的大都用不掉remainVolumn这么多 【预测性可行性剪枝】

maxV(level,maxRadius,maxHeight)<remainVolumn  //用level层蛋糕,第level层最大半径maxRadius,最高高度maxHeight时耗费的最多体积

2.剩下的蛋糕尽可能小都超过remainVolumn这么多 【预测性可行性剪枝】

volumn[level] > remainVolumn

3. maxRadius或maxHeight小于当前的层数,那可预见往上摆的时候肯定有一层蛋糕枚举不出来 【预测性可行性剪枝】

4. 目前已产生面积,加上之后level层的最小侧面积超过已经搜索到的答案【全局最优性剪枝】

area + minArea[level] > ans //维护minArea[i]数组代表第i层到第1层蛋糕所用最小【侧面积】

 

注意:所有上表面积都投影下来可以看成第m层蛋糕的上表面积

因为剪枝这种东西时间复杂度没法分析,所以写了这么多剪枝,也不知道哪个剪枝剪掉的东西最多。所以就是暴力大比拼了。

 1 #include<iostream>
 2 #include<cmath>
 3 using namespace std;
 4 
 5 int volumn[25],minArea[25];//volumn[i]为第i层到第1层蛋糕所用最小体积
 6 int ans,n,m;                    //minArea[i]为第i层到第1层蛋糕所用【最小侧面积】 
 7 int area;//当前dfs状态所需要用的表面积
 8 
 9 int maxV(int level,int r,int h){//用level层蛋糕用的最多体积
10                                 //在第level层的半径为r,高度为h的情况下
11     int v=0;
12     for(int i=0;i<level;i++){
13         v+=r*r*h;
14         r--; h--;
15     }
16     return v;
17 }
18 
19 void dfs(int level,int remainVolumn,int maxRadius,int maxHeight){//用level层蛋糕去凑体积remainVolumn 
20                                                     //这一层蛋糕可枚举的最大半径是maxRadius,最高高度是maxHeight    
21 //    cout<<level<<" "<<remainVolumn<<" "<<maxRadius<<" "<<maxHeight<<endl;
22     if(level==0){
23         if(remainVolumn) return;
24          ans=min(ans,area);
25          return;
26     }
27     
28     if( volumn[level]>remainVolumn ) return;//如果剩下的层数用最小的体积去摆都会超体积 == 预测可行性剪枝 
29     if( area+minArea[level]>ans ) return;    //全局最优性剪枝 
30     if( maxRadius<level || maxHeight<level ) return;//可行性剪枝
31     if( remainVolumn>maxV(level,maxRadius,maxHeight)) return;//如果剩下的层数用尽可能大的体积去摆都用不完体积 == 预测可行性剪枝
32     
33     //剪完枝开始枚举第level层的【半径】和【高度】 
34     for(int i=maxRadius;i>=level;i--){
35         if( level==m ) area=i*i;//把底面积考虑上
36         for(int j=maxHeight;j>=level;j--){
37             area+=2*i*j;
38             if( remainVolumn-i*i*j>=0) {
39             //    cout<<"!!! "<<i<<" "<<j<<endl;
40                 dfs(level-1,remainVolumn-i*i*j,i-1,j-1);
41             }
42             area-=2*i*j;
43         }
44     }
45     
46 }
47 
48 int main(){
49     
50     while( scanf("%d",&n)!=EOF ){
51         cin>>m;
52         ans=(1<<30);
53     
54         for(int i=1;i<=m;i++){
55             volumn[i] = volumn[i-1] + i*i*i;
56             minArea[i] = minArea[i-1] + 2*i*i;
57         }
58         area=0;
59         
60         //半径最大时是第一层蛋糕体积最大且高度最小 
61         //1.什么时候体积最大
62         //2.什么时候高度最小 
63         int maxRadius = sqrt( (n-volumn[m-1])/m );
64         
65         //高度最高是第一层蛋糕体积最大且半径最小
66         //第i层半径最小是i 
67         int maxHeight = (n-volumn[m-1]) / (m*m) ;
68         
69         dfs(m,n,maxRadius,maxHeight);
70         
71         if(ans==(1<<30)) cout<<0<<endl;
72         else cout<<ans<<endl;
73     }
74 
75     
76     return 0;
77 }

 

posted @ 2018-07-26 12:24  4397  阅读(280)  评论(0编辑  收藏  举报