[POJ1190][NOI1999]生日蛋糕(搜索+剪枝)

传送门


首先读题:
  本题忽略 π π 只将它后面的有理数进行计算,其次对于表面积,整个蛋糕的上表面面积之和等于最大圆的底面积。所以我们只需要计算侧面积,最底那层计算底面积即可。

  仍然是一道经典的搜索剪枝题目。我首先把题目所描述的(1~M从底向上)倒过来(1~M从上到底)方便处理,然后我们从下(最大那层)往上搜索。

搜索思路:
  那么我们首先爆搜,我们由于高度和半径下一层都要大于上一层,可以通过记录上一层的半径和高枚举当前一层的半径和高。枚举的下界自然就是当前层的层数(以半径为例,假设第一层半径为1,因为第二层的半径就最少比他大1,那么每一层最小的半径就是层数),枚举的上界呢就是上一层的半径或者高度减一。 但是最大那层的半径和高度的上限应该是多少呢?我们从极端情况考虑,当体积 N N 不变,半径r最大时,高度 h h 最小为1,那么r最大为 N N ,同理可得 h h 最大为N

剪枝:
  爆搜的思路大概就是这样,那么我们显然发现这样慢的不行,考虑几个剪枝。
1、枚举上限:我们可以通过圆柱体体积计算公式 πr2h=π(NsumV)[sumV] π r 2 h = π ( N − s u m V ) [ s u m V 为 当 前 体 积 和 ] 得到:
ri r i 的枚举范围为 depi d e p i min(NsumV,ri11) m i n ( N − s u m V , r i − 1 − 1 )
hi h i 的枚举范围为 depi d e p i min(NsumVr2,hi11) m i n ( N − s u m V r 2 , h i − 1 − 1 )

2、在第一条的枚举中倒序搜索。因为这样我们可以尽量快的找到合法状态。

3、还需要一些可行性剪枝。我们预处理到每层最小的体积和和最小的侧面积和,//如果当前体积加上剩下层的最小体积>N或者当前表面积加上剩下层的最小表面积>最优解就剪枝 。相当于看到前面是墙就转弯。

4、还是一个可行性剪枝,如果把剩下的体积全部使用,在表面积最小的情况下仍大于最优解,那么可以剪枝。这个剪枝需要推导一下公式。
2rh=S 2 ∗ r ∗ h = S
rrh=V r ∗ r ∗ h = V
V2/r=S 即 V ∗ 2 / r = S
S+sumS= S + s u m S =
V2/r+sumS= V ∗ 2 / r + s u m S =
(NsumV)2/r+sumS>=minn ( N − s u m V ) ∗ 2 / r + s u m S >= m i n n 时return

// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
int M,N;
int minn;
int minV[20],minS[20];
/*体积V = r^2h(pi)
侧面积A' = 2rh(pi) 
底面积A = r^2(pi)*/
//从上往下 1~M层 
void dfs(int now,int V,int S,int lastr,int lasth)
{//当前层数Now ,目前体积V,目前表面积S,上一层半径lastr,上一层高lasth 
    if(now==0)
    {
        if(V==N && minn>S)
            minn=S;
        return;
    }
    if(V+minV[now]>N || S+minS[now]>minn)
        return;//如果当前体积加上剩下层的最小体积>N或者当前表面积加上剩下层的最小表面积>最优解就剪枝 

    if( int(2*(N-V)/lastr) + S >= minn) 
        return;//把剩下的所有体积,全部使用表面积大于最优解 
    for(int i=min(int(sqrt(N-V)),lastr-1);i>=now;i--)//枚举下一层的半径
    {
        if(now==M) S=i*i;
        for(int j=min(int((N-V)/i*i),lasth-1);j>=now;j--)
        {
            dfs(now-1,V+i*i*j,S+i*j*2,i,j);
        }
    } 
}
int main()
{
    scanf("%d%d",&N,&M);
    minV[0]=minS[0]=0;
    for(int i=1;i<=M;i++)//预处理到某一层的最小侧面积和最小体积 
    {
        minV[i]=minV[i-1]+i*i*i;
        minS[i]=minS[i-1]+2*i*i;    
    }
    minn=999999999;
    dfs(M,0,0,sqrt(N),N);//当N时r最大为sqrt(N),h最大为N 
    if(minn==999999999) minn=0;
    printf("%d\n",minn);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值