poj_1190_剪枝训练

生日蛋糕
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 16428 Accepted: 5836

Description

7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。 
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。 
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。 
令Q = Sπ 
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。 
(除Q外,以上所有数据皆为正整数) 

Input

有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。

Output

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

Sample Input

100
2

Sample Output

68

Hint

圆柱公式 
体积V = πR 2
侧面积A' = 2πRH 
底面积A = πR 2 

/*************************

思路:把所有可能罗列出来,得到最小表面积。做题过程中要考虑从哪里开始,到哪里结束;

也就是说要考虑到初始状态和结束状态,而每次搜索状态的改变都会对应改变一些变量的值,

1.这些变量往往就是搜索时要着重考虑的。

比如加在函数中dfs(int a,int b,int c,int d),

比如每次搜索中sum++或minS = min(minS,nowS);

2.深搜重在一个深字,一条路走到底,所以每次搜索 初始化 和 回溯 也很重要


PS:一开始竟然超时,后来根据高人指点加了剪枝5,神剪枝啊

*************************/

<span style="font-size:14px;">#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <bitset>
#include <list>
using namespace std;

#define INT_MAX 1 << 30

typedef long long ll;
int N,M;
int Vmin[30];
int Smin[30];
int maxR,maxH;
int minV;
int nowV;
int minS;
int nowS;

int maxV(int nn,int rr,int hh)
{
    int vv = 0;
    for (int i = 0; i < nn; i += 1)
    {
        vv += (rr-i)*(rr-i)*(hh-i);
    }
    return vv;
}

void dfs(int v, int m, int maxR, int maxH)
{   
    if ((m == 0) && (v == 0))                   //循环结束条件判断
    {
        minS = min(minS,nowS);
        return ;
    }
    if ((v <= 0) || (m <= 0))                   //剪枝2,其中一值为负则跳出
    {
        return ;
    }
    if (nowV >= N)                              //剪枝3,第一层未搜完便溢出,跳出
    {
        return ;
    }
    if (nowS >= minS)                           //剪枝4,第一层未搜完便溢出,跳出
    {
        return ;
    }
    if(maxV(m,maxR,maxH) < v)                   //剪枝5,不仅考虑溢出,还要考虑不足
    {
        return ;
    }
    for (int r = maxR; r >= m; r -= 1)          //遍历循环
    {
        for (int h = maxH; h >= m; h -= 1)
        {
            if (m == M)
            {
                nowS = r*r;
                nowV = 0;
            }
            nowS += 2*r*h;                      //下次循环初始化
            nowV += r*r*h;
            dfs(v-r*r*h,m-1,r-1,h-1);           //循环
            nowS -= 2*r*h;                      //回溯
            nowV -= r*r*h;
        }
    }
}

int main()
{
    cin >> N >> M;                              //输入及存储
    Smin[0] = M*M;
    Vmin[0] = 0;
    minV = 0;
    for (int i = 0; i <= M; i += 1)
    {
        Vmin[i] += i*i*i;
        Smin[i] = Smin[i-1] + 2*i*i;
    }
    if(Vmin[M] > N)                             //剪枝1
        printf("0\n");
    else
    {
        int H = (N-Vmin[M-1])/(M*M) + 1;
        int R = (int)sqrt((double)((N-Vmin[M-1])/M))+1;
        minS = (1 << 30);                       //初始化
        dfs(N,M,R,H);                           //搜索
        printf("%d\n",minS);                    //输出
    }
    return 0;
}</span>

一开始编写的时候在遍历搜索里写错了 dfs,把 maxR, maxH 仍然按照主函数的方法写,但是输出值总是比标准答案小,后来想到是没有考虑到:

m-1 层的半径用这种方式计算可能比m层的半径大。。。那样很可能形成的是一个类似菱形旋转形成的几何体或是什么乱七八糟的东西。。。

因此乖乖在搜索中用 r-1, h-1 进一步搜索。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值