生日蛋糕

题目描述

7 7 7 17 17 17 日是 M r . W Mr.W Mr.W 的生日, A C M − T H U ACM-THU ACMTHU 为此要制作一个体积为 N N Nπ 的 M M M 层生日蛋糕,每层都是一个圆柱体。设从下往上数第 i ( 1 ≤ i ≤ M ) i(1 \leq i \leq M) i(1iM) 层蛋糕是半径为 R i R_i Ri , 高度为 H i H_i Hi 的圆柱。当 i < M i < M i<M_i<M iMiM 时,要求 R i > R i + 1 R_i>R_i+1 Ri>Ri+1 H i > H i + 1 H_i>H_i+1 Hi>Hi+1 。由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q Q Q 最小。

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

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

输入格式

有两行,第一行为 N   ( N ≤ 10000 ) N \ (N \leq 10000) N (N10000) ,表示待制作的蛋糕的体积为 N N Nπ ;第二行为 M   ( M ≤ 20 ) M \ (M \leq 20) M (M20),表示蛋糕的层数为 M M M

输出格式

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

输入样例

100
2

输出样例

68

样例说明

附:圆柱公式体积 V = π R 2 H V=πR^2H V=πR2H ,侧面积 A = 2 π R H A=2πRH A=2πRH ,底面积 A = π R 2 A=πR^2 A=πR2

解题思路

DFS+剪枝

标准程序

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
//minv 和 mins 数组表示1~20层每层最小的体积和最小的侧面积
int n, m, minv[25], mins[25], ans = 1e9;

// v 当前体积  s 当前面积  dep 当前层次  r 最大半径  h 最大高度
void dfs(int v, int s, int dep, int r, int h) {
    //递归出口
    if (dep == 0) {
        //制作到m层,且体积等于 n 取结果的最小值
        if (v == n) ans = min(ans, s);
        return;
    }
    //-----剪枝操作start--------//
    //当前体积+最小体积都大于n,则方案不可行
    if (v + minv[dep - 1] > n) return;  
    //当前面积+最小面积大于当前最优解 则该方案一定不是最优解
    if (s + mins[dep - 1] > ans) return;
    //当前面积+剩余的面积大于等于当前最优解,则该方案一定不是最优解
    //注意包含等于,为何?因为剩余的最大面积是在半径相同的情况下,而题目要求半径是递减的
    if (2 * (n - v) / r + s >= ans) return;
    //-----剪枝操作end---------//
    //枚举可能的半径,注意半径的最小值应该是当前层次
    for (int i = r - 1; i >= dep; i--) {
        //如果是最后一层直接+上表面面积,因为蛋糕是累加的,从上面看就是最后一层的面积。
        //其他层其实只增加表面积
        if (dep == m) s = i * i; 
        //获取高度的最大值,剩余体积/面积 ,当然还要确保满足大于等于h-1
        int hh = min(h - 1, (n - v - minv[dep - 1]) / (i * i));
        //枚举可能的高度
        for (int j = hh; j >= dep; j--)
            dfs(v + i * i * j, s + 2 * i * j, dep - 1, i, j); //dfs
    }
}

int main() {
    scanf("%d%d", &n, &m);
    //预处理 20层每层最小体积和最小侧面积。
    //因为上次层高度和半径差最小值是 1,所以直接枚举一下 
    for (int i = 1; i <= 20; i++) {
        minv[i] = minv[i - 1] + i * i * i; // 前两个i表示半径 后一个i表示高度 计算体积
        mins[i] = mins[i - 1] + 2 * i * i; // 第一个i表示半径 后一个i表示高度 计算侧面积
    }
    //sqrt(n) 对半径做了优化
    dfs(0, 0, m, sqrt(n), n + 1);  //dfs
    if (ans >= 1e9)
        printf("0\n");
    else
        printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值