反正想清楚了就开始枚举吧,需要拿纸和笔推一些公式。
我们想要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 }