(poj1190)生日蛋糕(DFS和数学知识,剪枝!)

18 篇文章 0 订阅

题目描述
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外,以上所有数据皆为正整数)
输入
有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。
输出
仅一行,是一个正整数S(若无解则S = 0)。
样例输入
100
2
样例输出
68
提示
圆柱公式

体积V = πR2H

侧面积A’ = 2πRH

底面积A = πR2

分析:这道题,一开始看着,这题放在搜索专题里,这怎么搜索,不好下手,细看题目条件
当i < M时,要求Ri > Ri+1且Hi > Hi+1。 (除Q外,以上所有数据皆为正整数),这两句话比较关键,而且给出了N <= 10000,结合V=pi*R*R*H,可以知道,r最大为100,h最大为10000,那么可以从下往上搜索,DFS,处理好了剪枝就无敌了,没处理好,一直TLE。
这道题的剪枝比较多,开始没有想到将体积和面积的公式联系起来
V=pi*r*r*h,A’=2*pi*r*h; A’=2*V/r;

求和公式:
1^3+2^3+……+n^3=(n(n-1)/2)^2
直接用此公式比下面的循环打表快一点 (MINV[]数组)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int MINS,n,m,MINV[22];
#define pow(x) ((x)*(x))
#define sum3_V(x) ((pow((x))*pow((x)+1))>>2)
void dfs(int level,int leftV,int Ri,int Hi,int S)
{
    if(level==0&&leftV==0)///蛋糕刚好用完
    {
        MINS=min(MINS,S);
        return ;
    }
    if(leftV<=0||level<=0) return ;///剪枝,不符合实际的情况
    if(S>=MINS)///剪枝,当前面积大于最优解
        return ;
    if(leftV<sum3_V(level-1))///剪枝,剩余的蛋糕不够用,去掉这个判断不会超时
        return ;
    for(int i=Ri-1; i>=level; i--)
    {
        int H=min(Hi-1,(leftV-sum3_V(level-1))/(i*i));
        ///H为当前能取的高度的上界,leftV为当前剩余的体积,MIN[level-1]为上面所有的体积之和
        ///根据V=pi*r*r*h,和A=pi*r*r的关系得到
        for(int j=H; j>=level; j--)
        {
            int curV=i*i*j;
            int curA=2*i*j;
            if(2*leftV/Ri+S>=MINS) continue;///未用完的蛋糕所构成的面积大于最优解,强力剪枝
            if(level==m) curA+=i*i;///当前层是最下面的一层
            dfs(level-1,leftV-curV,i,j,S+curA);
        }
    }
}
int main()
{
    //freopen("E:\\in.txt","r",stdin);
    /*memset(MINV,0,sizeof(MINV));
    for(int i=1; i<22; i++)
        MINV[i]=MINV[i-1]+i*i*i;*/
    while(~scanf("%d%d",&n,&m))
    {
        MINS=INF;
        dfs(m,n,100,10000,0);
        if(MINS==INF)
            puts("0");
        else
            printf("%d\n", MINS);
    }
    return 0;
}

感受:像这个题,乍一看,觉得不是搜索,但是,还是要仔细的读题,分析题目给出的约束条件(例如范围之类),其实还是有一定的规律可循,剪枝,这个就得多分析,从题目意思入手,联系题目中各个数据的关系,本题中的体积,面积,和高度之间的关系恰恰是比较强力的剪枝

另外,这道题学校oj的数据和poj的不一样,应该是学校加强了数据,然后看的别人的题解报告,写得代码WA掉了,感觉写出来没什么区别吧

下面的这份代码中少了一个h的取上界,就在学校oj超限了,poj上可以过,两个oj数据强度不一样

#include<cstdio>
using namespace std;
int ans=0,N,M;
void dfs(int m,int leftv,int sum_S,int last_r,int last_h)
{
    if(!m&&!leftv)///蛋糕恰好用完并且是m层
    {
        if(sum_S<ans||!ans) ans=sum_S;
        return;
    }
    if(m*(last_r-1)*(last_r-1)*(last_h-1)<leftv&&m!=M) return;///剩余体积比最大可能体积大
    if(leftv<=0||m<=0||(sum_S>=ans&&ans)) return; ///不符合实际的情况和当前面积大所求的最小面积
    if(ans&&sum_S>=ans) return ;///当前面积比最小的大
    int r,h,curV,curA;
    for(r=last_r-1; r>=m; r--)
        for(h=last_h-1; h>=m; h--)
        {
            curV=r*r*h; ///当前层体积
            curA=2*r*h; ///当前层侧面积
            if(m==M) ///若为最下面的一层,加上底面积
                curA+=r*r;
            if(sum_S+2*leftv/r>=ans&&ans)
                continue;
            dfs(m-1,leftv-curV,sum_S+curA,r,h);
        }
}
int main()
{
    while(~scanf("%d%d",&N,&M))
    {
        dfs(M,N,0,100,10000);
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值