详解 poj 1190 深搜 加 剪枝

来源:互联网 发布:申请网络记者 编辑:程序博客网 时间:2024/06/08 06:36

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

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

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

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

                      (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;}

                         

0 0
原创粉丝点击