生日蛋糕(深搜+剪枝)

问题

7 月 17 日是 Mr.W 的生日,ACM-THU 为此要制作一个体积为 NπNπ 的 M 层生日蛋糕,每层都是一个圆柱体。

设从下往上数第 i 层蛋糕是半径为 Ri,高度为 Hi 的圆柱。

当 i<M 时,要求 Ri>Ri+1 且 Hi>Hi+1。

由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 QQ 最小。

令 Q=Sπ ,请编程对给出的 N 和 M,找出蛋糕的制作方案(适当的 Ri 和 Hi 的值),使 S 最小。

除 Q 外,以上所有数据皆为正整数。

输入格式

输入包含两行,第一行为整数 N,表示待制作的蛋糕的体积为 Nπ。

第二行为整数 M,表示蛋糕的层数为 M。

输出格式

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

数据范围

1≤N≤10000,
1≤M≤20

输入样例:

100
2

输出样例:

68

思路:

    总面积 = 求每层蛋糕的侧面积+最底层蛋糕的上表面积

    注意:由题干信息可知,本题中可以忽略Pi。
    1.从下往上搜,每次枚举采取块大的先枚举,这样方案数少。

	2.每一层的高度h和半径r,应该如何枚举? 即高度h和半径r的上下界是多少? 
    假设当前体积为v,总体积为n , 剩余体积 n-v
    圆柱体积为 r*r*h 
    若要使 r 尽可能大,则令h=1即可,r <= sqrt(n-v)
    当r确定了后, h = (n-v)/(r*r) [下取整]
    因此,
        R的上界2种情况
        a. Ri>Rj,(i<j), 当前层半径小于上一层半径(pr),即 pr-1
        b. sqrt(n-v)
        最后取2种情况的最小值,即 r<= min(sqrt(n-v),pr-1)
        R的下界
        只需要大于等于当前层,即 r>=depth

        同理,H的上界
        H的上界2种情况
        a. Hi>Hj,(i<j), 当前层高度小于上一层高度(ph),即 ph-1
        b. (n-v)/(r*r) [下取整]
        最后取2种情况的最小值,即 r<= min((n-v)/(r*r),ph-1)
        H的下界
        只需要大于等于当前层,即 H>=depth

	3.剪枝:
	    1.如果当前搜索到的最小表面积已经大于了已知的最小表面积直接退出
        2.如果当前搜索到的体积已经超过题目限制则退出
        3.如果由体积推出来当前的表面积已经不优直接退出

参考代码:

#include <bits/stdc++.h>
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define LL long long
#define PII pair<int,int>
#define PIII pair<int,PII>
#define PSI pair<string,int>
#define PIIS pair<int,pair<int,string> >
#define PDD pair<double,double>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=2e5+5;
const int M=1e7;
const int mod=1e9+7;

int n,m; //体积和高度
int ans; //最优面积
int R[22],H[22]; //每层的半径和高度
int minv[22]; //每一层最小体积
int mins[22]; //每一层最小面积

//s当前面积,cur剩余层数,v当前体积 
void dfs(int s,int cur,int v)
{
    //3种剪枝情况
    //如果当前搜索到的体积已经超过题目限制则退出
	if(v+minv[cur]>n)return ;
    //如果当前搜索到的最小表面积已经大于了已知的最小表面积直接退出
    if(s+mins[cur]>=ans)return;
    //如果由体积推出来当前的表面积已经不优直接退出
    if(s+2*(n-v)/R[cur+1]>=ans)return;


	if(cur==0) //满足条件,更新最优解 
	{
        if(v==n)ans=s;
		return ; 
	} 

    //搜索顺序先R后H,从大到小
    //枚举半径
    for(int i=min(R[cur+1]-1,(int)sqrt(n-v));i>=cur;i--)
    {
        //枚举高度
        for(int j=min(H[cur+1]-1,(n-v)/i/i);j>=cur;j--)
        {
            //记录当前层的R和H
            R[cur]=i;
            H[cur]=j;
            
            int t=0;
            if(cur==m)t=i*i; //加上最底层的表面积
            int cs=2*i*j; //使用了侧面积
            int cv=i*i*j; //使用了体积
            dfs(s+t+cs,cur-1,v+cv);
        }

    }
}

int main()
{
//	io;
	cin>>n>>m;
    //初始化minv和mins,若要使每层面积和高度最小,则取当前层值
    //即 r=h=i
    for(int i=1;i<=m;i++)
    {
        minv[i]=minv[i-1]+i*i*i;
        mins[i]=mins[i-1]+2*i*i;
    }
	ans=INF; 
    //边界值,减少边界情况判断
    R[m+1]=INF,H[m+1]=INF;
	dfs(0,m,0);
    cout<<(ans==INF?0:ans)<<"\n";

    system("pause");
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值