详解 poj 1190 深搜 加 剪枝

这道题用到 深搜 但是必须剪枝,否则一定超时~~

我把注释都写在程序上了,大家可以一边看一边理解~~

剪枝地方还是要说明一下:

         遇到三种情况我们就不必搜下去了,需要剪掉:

                      (1) v + minv[m-1] > N      当前的体积,加上该层以上的最小体积都比总体积大,一定不符合条件!

                      (2) s + mins[m-1] > ans   当前的表面积加上该层以上的最小表面积比当前最优解大,一定不符合条件!

                       (3) 2*(N-v)/r + s >= ans    我们求到第m层的体积和表面积了,那么剩余的体积leftv(也就是该层以上的体积)就是 : N - v 。

                     我们现在假设m层以上是一个圆柱体,并且半径跟该层的半径 r 一样,那么  lefts =  2 * (N - v)  /  r   (v = pi × r × r ×  h ;

                     s =2  × pi ×  r ×  h,根据这2个公式就可以推出)    但是!!实际情况是,该层以上的半径一定小于这一层

                     的半径 r !我们看到分母越小,整个的值就越大!所以,lefts 一定 大于   2×(N - v)/  r   ( lefts >  2 * (N - v)  /  r  一定成立! )

                      而 lefts 还 等于  ans (最优解,即最终的答案) - s  !!  也就是  lefts  = ans - s  >  2 * (N - v)  /  r 

                     所以 2*(N-v)/r + s 一定小于 ans   即 2*(N-v)/r + s < ans

                     所以如果  2*(N-v)/r + s >= ans   就可以剪掉!




#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define inf 10000000
#define min(a,b) (a < b ? a : b)
using namespace std;
int N,M;                  //蛋糕的体积和层数 
int minv[21],mins[21];    //用来表示蛋糕最小的体积和表面积 
int ans;                  //最终结果 ,也就是最优解,也就是 Q的最优解,也就是 S的最优解  
void init(){              //初始化2个数组,表示第i层上的最小体积和最小表面积 
	minv[0] = 0;           
	mins[0] = 0;
	for(int i = 1; i <= 20; i++){    //体积和表面积最小的时候,就是从最上面一层r=h=1,接着下一层是r=h=2, 
		minv[i] = minv[i-1] + i*i*i;  //以此类推,第i层就是r=h=i   题目最多20层,所以这20层的最小体积和 
		mins[i] = mins[i-1] + 2*i*i;  //最小表面积都可以求出来,这是最小的情况,不能再比他小了 !! 
	}                                 //  我们可以用这个做临界条件,进行剪枝 
}
void dfs(int m,int v,int s,int r,int h){   //从最下面一层向上深搜。好处就是可以知道最下面一层的半径,从而知道整个蛋糕的上表面积 
                                            //其中m表示当前层数,v表示当前体积,s表示当前表面积    
    if(m == 0){                            //r代表当前层的半径,h表示当前层的高 
    	if(v == N && s < ans){
    		ans = s;
    	}
    	return;
    }
    if( v + minv[m-1] > N || s + mins[m-1] > ans || 2*(N-v)/r + s >= ans) return;  //剪枝 
    for(int i = r - 1; i >= m; i--){     //按照递减序列枚举第m层的所有可能情况,
	                                     //最大的半径是r-1(r是上一层的半径,为了符合要求就是该层半径就是r-1
										 //最小半径是m,m是该层的最小表面积与最小体积时的半径(见init()的注释) 
    	if(m == M)
    		s = i*i;    //当前层是第M层时,它的半径就是整个蛋糕的上表面积,先加上再说~~ 这样以后只需加侧面积就可以了 
    	int maxh = min((N-v-minv[m-1])/(i*i),h-1);  //最大高度。第m层的最大体积的高:
		// ( n - v(该层的下面的层的体积) - minv[m-1] (该层的上面的层的最小体积))/ i*i (i是当前层的半径) 
    	for(int j = maxh; j >= m; j--){   //h的最大值与最小值同r情况一样 
    		dfs(m-1,v+i*i*j,s+2*i*j,i,j);  //每深搜一次减小一层,要加上该层的体积和侧面积 
    	}
    }
}
int main(int argc, char *argv[]){
	init();
    while(~scanf("%d%d",&N,&M)){
    	ans = inf;
    	dfs(M,0,0,N+1,N+1);        // 蛋糕的总体积是N,所以它的半径和高再高也不可能高于N,所以从N开始深搜 
    	if(ans == inf) ans = 0;     //如果最后ans的值没有变,说明找不到这样的解,S无解,输出0 
    	printf("%d\n",ans);
    	
    }
	return 0;
}


                         

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值