【YbtOJ】生日蛋糕

【YbtOJ】生日蛋糕

好家伙这在洛谷是道蓝题

题面

题目描述

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

设从下往上数第 \(i(1 \leq i \leq M)\)层蛋糕是半径为 \(R_i\),高度为 \(H_i\)的圆柱。当 \(i \lt M\)时,要求 \(R_i \gt R_{i+1}\)\(H_i \gt H_{i+1}\)

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

请编程对给出的 \(N\)\(M\),找出蛋糕的制作方案(适当的 \(R_i\)\(H_i\) 的值),使 \(S=\dfrac{Q}{\pi}\)最小。

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

输入格式

第一行为一个整数 \(N(N \leq 2 \times 10^4)\),表示待制作的蛋糕的体积为 \(N\times \pi\)

第二行为 \(M(M \leq 15)\),表示蛋糕的层数为 \(M\)

输出格式

输出一个整数 \(S\),若无解,输出 \(0\)

样例输入

100
2

样例输出

68

分析

  • 蛋糕的表面积为各层侧面积之和加上底层的上表面面积

  • 基本的思路其实很简单

    从下向上搜索每一层的高度 \(h\) 与半径 \(r\) 并记录目前的总表面积 \(s\) 、还需要的体积\(v\)以及当前层数

    1. 如果已经到达了最顶层,检查 \(v\) 是否为 \(0\) ,即体积是否等于 \(m\)
      1. 若体积恰巧等于 \(m\) 则找到一种解,比较 \(s\) 与目前最小表面积 \(minS\)
      2. 若体积不相等,则这种方法不符合规则应舍弃
    2. 枚举 \(h\)\(r\) ,判断侧面积 \(s'\) 、 目前体积 \(v'\) 是否合法,不合法就跳过
  • 所有式子里面都有 \(\pi\) 可以将其提出来,在运算过程中忽略掉

剪枝

  • 先给出我最开始写出来的、显而易见的剪枝

    1. 若目前的总表面积已经超过了 \(minS\) ,很显然最后的结果一定会超过目前的最小值
    2. 如果加上该层蛋糕的体积后总体积超过所求体积,最后答案一定非法
  • 然后是书中给出的剪枝方法(

    记当前表面积为 \(S\) ,当前体积 \(V\) ,当前处于 \(dep\) 层(最上层为第 \(1\) 层)

    1. 上下界剪枝

      根据 \(\pi R^2H=\pi(N-V)\) 可得

      1. \(R = \sqrt{\frac{N-V}{H}}\)

        由于 \(H\) 最小为 \(1\) ,则 \(R\) 的最大值为 \(\sqrt{N-V}\)

        但考虑到 \(R_{dep}\) 不能超过上一层的 \(R_{dep+1}\)

        因此 \(R_{dep}\) 的最大值为 \(\min(R_{dep+1},\sqrt{N-V})\)

        由于每一层的 \(R_{dep}\) 都需要小于上一层的 \(R_{dep+1}\) ,可知 \(R_{dep}\) 的最小值就是 \(dep\)

        因此,\(R\) 的取值范围是 \([dep,\min(R_{dep+1},\sqrt{N-V})]\)

      2. \(H=\frac{N-V}{R^2}\),由于是先枚举 \(R\) 再枚举 \(H\) ,因此这个 \(R^2\) 可以留下来

        其余部分同 \(R\)

        因此 \(H\) 的取值范围为 \([dep,\min(R_{dep+1},[\frac{N-V}{R^2}]]\)

    2. 优化搜索顺序

      在上面确定的范围中,采用倒序枚举

      因为该层的 \(R\)\(H\) 越大,留给接下来的蛋糕的 \(R\)\(H\) 的范围就越大

    3. 可行性剪枝

      可以预处理出从上往下前 \(i\) 层的最小体积和侧面积,显然,当第 \(1\) ~ \(i\) 层的半径分别取 \(1,2,3,\cdots,i\) ,高度也分别取 \(1,2,3,\cdots,i\) 时,有最小体积和侧面积

      如果当前体积 \(V\) 加上 \(1\sim dep-1\) 层的最小体积大于 \(N\) ,可以剪枝

    4. 最优性剪枝一

      如果当前表面积 \(S\) 加上 \(1\sim dep-1\) 层的最小侧面积大于已经搜到的答案,剪枝

    5. 最优性剪枝二

      (下列描述忽略 \(\pi\)

      利用 \(h\)\(r\) 数组, \(1\sim dep-1\) 层的体积可表示为 \(N-V=\sum^{dep-1}_{k=1}(h_k\times r_k^2)\)

      \(1\sim dep-1\) 层的侧面积可表示为 \(2\sum^{dep-1}_{k=1}(h_k\times r_k)\)

      因为

      \[\begin{align} 2\sum^{dep-1}_{k=1}(h_k\times r_k)&=\dfrac{2}{r_{dep}}\times\sum^{dep-1}_{k=1}(h_k\times r_k \times r_{dep})\\ &\geq\dfrac{2}{r_{dep}}\times \sum^{dep-1}_{k=1}(h_k\times r_k^2)\\ &\geq\dfrac{2(N-V)}{r_{dep}}\\ \end{align} \]

      所以当 \(\dfrac{2(N-V)}{r_{dep}}+S > minS\) (即目前的 \(S\) 加上上面的所有层侧面积最小值仍然大于已经搜到的答案)时,可以剪枝

代码

#include <bits/stdc++.h>
using namespace std;

int n, m;
int minS = 1e9;

inline int CS(int r, int h) { return 2 * r * h; }
inline int CV(int r, int h) { return r * r * h; }
void dfs(int N, int R, int H, int cnt, int sc) {
    if (sc > minS)
        return;
    if (cnt == m + 1) {
        if (N != 0)
            return;
        minS = min(minS, sc);
        return;
    }
    int k = m - cnt + 1;
    if (k * R * R * H < N)
        return;
    if (k * 2 + sc > minS)
        return;
    if (cnt == 1) {
        for (int r = R; r >= m; r--) {
            for (int h = m; h <= H; h++) {
                int cs = CS(r, h);
                int cv = CV(r, h);
                if (N - cv < 0)
                    continue;
                if (r * r * h > n)
                    continue;
                dfs(N - cv, r, h, cnt + 1, sc + cs + r * r);
            }
        }
    } else
        for (int r = R - 1; r >= m - cnt + 1; r--) {
            for (int h = m - cnt + 1; h < H; h++) {
                int cs = CS(r, h);
                int cv = CV(r, h);
                if (N - cv < 0)
                    continue;
                if (r * r * h > n)
                    continue;
                dfs(N - cv, r, h, cnt + 1, sc + cs);
            }
        }
}
int main() {
    scanf("%d%d", &n, &m);
    dfs(n, 50, 50, 1, 0);
    printf("%d", minS);
    return 0;
}

posted @ 2022-02-21 10:30  Burnling  阅读(154)  评论(0编辑  收藏  举报