POJ 1190 生日蛋糕

17 篇文章 0 订阅

生日蛋糕

一道很经典也很基础的搜索题目。有题目的条件我们可以得到两个方程:sum(Rk*Rk*hk) = N 和 S = sum(2*Rk*hk)+R1*R1

首先考虑极端剪枝法。假设前i层体积为T,如果剩下的若干层,每层都去最小可能值,体积仍比N大,则剪去,如果剩下几层都去最大值,体积仍比N小,也减去。如果当前的表面积比S大,则剪去。

然后考虑一个隐藏的条件,我们可以由式子sum(Rk * Rk * hk) = N来考虑。假设前i层的体积为T * PI,表面积为W * PI ,那么剩余m-i层的体积满足: N—T = sum(Rk * Rk *hk),其中i+1 <= k <= m ,而剩余的表面积为:leftS = sum(2*Rk * hk) i+1<=k<=m ,lefts = 2sum(Rk*Rk*hk/Rk) >= 2*sum(Rk*Rk*hk/Ri) = 2*(N-T)/Ri= p ;如果p>= S-w

那么lefts >= s-w ,于是我们在这里也可以加以剪枝。只需要判断一下即可。(参考了刘汝佳的算法艺术与信息学竞赛)

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>

using namespace std ;

int N ;
int M ;
int S ;
int nmin ;

//leftv 表示剩余的体积 ,leftm剩余的层数
//lastr上一层的半径,lasth上一层的高度
void dfs(int leftv , int leftm , int lastr , int lasth){

    if(!leftm){
        if(leftv != 0)
            return ;
        if(nmin < S)
            S = nmin ;
        return ;
    }

    //计算当前条件下最小的体积
    int t ;
    t = 0 ;

    for(int i = leftm ; i > 0 ; i--){
        t += i * i * i ;
    }
    //剩余的最小体积仍大于剩下的体积
    if(t > leftv)
        return ;

    //计算剩下的最大半径
    int maxr ;
    int maxh ;

    t -= leftm * leftm * leftm ;
    maxr = (int)sqrt( 1.0 * (leftv - t)/leftm ) < lastr ? (int) sqrt(1.0 * (leftv - t)/leftm ) : lastr ;

    for(int r = maxr ; r >= leftm ; r --){
        maxh = (leftv - t) / (r*r) < lasth ? (leftv - t)/(r*r) : lasth ;

        for(int h= maxh ; h >= leftm ; h --){

            int maxv ;
            maxv = 0 ;
            //计算最大体积
            for(int i = 0 ; i <= leftm -1 ; i ++){
                maxv += (r - i) * (r - i) * (h - i) ;
            }
            if(maxv < leftv){
                break ;
            }


            //如果是第一层,则加上上表面积

            if(leftm == M){
                if(r * r < S)
                    nmin = r * r ;
                else
                    continue ;
            }

            if(nmin + 2 * r * h > S){
                continue ;
            }

            int v ;
            v = leftv - r * r * h ;
            //对应于第二种情况的剪枝
            if(2 * v / r >= S - nmin - 2 * r * h)
                continue ;

            nmin += 2 * r * h ;
            dfs(v , leftm - 1 , r - 1 , h - 1) ;
            nmin -= 2 * r * h ;
        }
    }

}

int main(){
    while(scanf("%d%d" , &N , &M) != EOF){
        nmin = 0 ;
        S= 1000000 ;
        dfs(N , M , 10000 , 10000) ;
        printf("%d\n" , S) ;
    }
    return 0 ;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值