搜索(1):剪枝

POJ 1190

要制作一个体积为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外,以上所有数据皆为正整数)
·
·
·

分析

·

深度优先搜索,枚举什么?

枚举每一层可能的高度和半径。

·
·

如何确定搜索范围

底层蛋糕的最大可能半径和最大可能高度

·
·

那些地方会体现搜索顺序

从底层往上搭蛋糕,而不是从顶层往下搭 在同一层进行尝试的时候,半径和高度都是从大到小试

·
·

如何剪枝

1:搭建过程中发现已建好的面积已经超过目前求得的最优表面积,或者预见到搭完后面积一定会超过目前最优表面积,则停止搭建 (最优性剪枝)

2:搭建过程中预见到再往上搭,高度已经无法安排,或者半径已经无法安排,则停止搭建(可行性剪枝)

3:搭建过程中发现还没搭的那些层的体积,一定会超过还缺的体积,则停止搭建(可行性剪枝)

4:搭建过程中发现还没搭的那些层的体积,最大也到不了还缺的体积,则停止搭建(可行性剪枝)
·
·

还有什么可以改进

1)用数组存放 MaxVforNRH(n,r,h) 的计算结果,避免重复计算

2)加上对本次Dfs失败原因的判断。如果是因为剩余体积不够大而失败,那么就用不着试下一个高度,直接break; 或者由小到大枚举 h
·
·

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

int N, M;
int minArea = 1 << 30;
int area = 0;
int minV[30];
int minA[30];
int minNRH[30][30][30];

int MaxVforNRH(int n, int r, int h)
{
    int v = 0;
    for (int i = 0; i < n; ++i)
        v += (r - i) *(r - i) * (h - i);
    return v;
}

int dfs(int v, int n, int r, int h)
{
    if (n == 0) {
        if (v) return 0;
        else {
            minArea = min(minArea, area);
            return 0;
        }
    }
    if (v <= 0)
        return 0;
    if (minV[n] > v) //剪枝3
        return 0;
    if (area + minA[n] >= minArea) //剪枝1
        return 0;
    if (h < n || r < n) //剪枝2
        return 0;
    if (MaxVforNRH(n, r, h) < v) 
        //剪枝4 //这个剪枝最强!没有的话,5秒都超时,有的话,10ms过! 
        return -1; 
    //for( int rr = n; rr <= r; ++ rr ) 这种写法比从大到小慢5倍 
    for( int rr = r; rr >=n; -- rr ) 
    { 
        if( n == M ) //底面积 
            area = rr * rr; 
        for( int hh = h; hh >= n ; --hh ) 
        { 
            int a;
            area += 2 * rr * hh; 
            a = dfs(v - rr*rr*hh, n - 1, rr - 1, hh - 1);
            area -= 2 * rr * hh; 
            if (a == -1)
                break;
        } 
    }
}

int main(void)
{
    scanf("%d %d", &N, &M);
    minV[0] = 0;
    minA[0] = 0;
    for (int i = 1; i <= M; i++)
    {
        minV[i] = minV[i - 1] + i * i * i;
        minA[i] = minA[i - 1] + 2 * i * i;

    }
    if (minV[M] > N)
        printf("0\n");
    else
    {
        int maxH = (N - minV[M - 1]) / (M * M) + 1;
        int maxR = sqrt((N - minV[M - 1]) / M * 1.0) + 1;
        area = 0;
        minArea = 1 << 30;
        dfs(N, M, maxR, maxH);
        if (minArea == 1 << 30)
            printf("0\n");
        else
            printf("%d\n", minArea);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值