[dfs] aw168. 生日蛋糕(dfs剪枝与优化+分类讨论+思维+公式推导+数学+好题)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[dfs] aw168. 生日蛋糕(dfs剪枝与优化+分类讨论+思维+公式推导+数学+好题)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:168. 生日蛋糕

2. 题目解析

非常非常经典的题目,优化很难…

首先的首先,理解题意,问题抽象。在总体积为 n,总层数为 m 的情况下确定每层的 rh,使得要去的蛋糕外表面积最小。


首先确定枚举顺序,枚举每层情况,再枚举每层的 rh,保证 rh 是递增的整数。枚举的种类是指数级别的,需要加入大量剪枝才可能通过本题。

优化:

  • 优化搜索顺序:从底向上枚举蛋糕每一层,因为底层占用体积大,后续分支少。每层中先枚举 r 再枚举 h 且也是从大到小枚举,因为 r 是平方级别,变化快。
  • 可行性剪枝与最优化剪枝:
    • 如果上一层半径和高度确定,那么对于本层来讲,半径最少是所在的层高,因为最小高度是 1,最大不能比下一层大。高度同样如此。故高度和半径都在枚举时有范围限制。
    • 如果前面所有层已经用了 v 体积和 s 表面积,那么如果后面所有层即便按照最小半径、高度分配也无法构成蛋糕或者更新最优解的话,也可以直接返回。
    • 推导体积和表面积公式之间的联系,这个是最难的,如果不加这个条件,在数据较强的情况下容易 TLE用体积估算前 u 层的最小表面积。

太难了。


具体剪枝推导,来自大佬

在这里插入图片描述


自己的手写笔记,比较乱:
在这里插入图片描述


时间复杂度: O ( 指 数 级 ) O(指数级) O()

空间复杂度: O ( n ) O(n) O(n)


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include  <cmath>

using namespace std;

const int N = 25;

int n, m;
int minv[N], mins[N];
int R[N], H[N];
int res = 1e9;

// 自底向上,处理第 u 层,v 为之前的总体积,s 为之前的表面积
void dfs(int u, int v, int s) {
    if (v + minv[u] > n) return ;           // 可行性剪枝
    if (s + mins[u] >= res) return ;        // 最优性剪枝
    if (s + 2 * (n - v) / R[u + 1] >= res) return ;     // 体积和面积关系,剪枝

    if (!u) {                               // 搜完所有层
        if (v == n) res = s;                // 如果体积恰好等于 n,则此时的 s 一定可以更新 res
        return ;
    }

    // 先枚举 r,再枚举 h,且从大到小枚举
    for (int r = min(R[u + 1] - 1, (int)sqrt(n - v)); r >= u; r -- ) 
        for (int h = min(H[u + 1] - 1, (n - v) / r / r); h >= u; h -- ) {
            int t = 0;
            if (u == m) t = r * r;  // 如果是底层,直接加上圆柱的红色面积,然后每层只需要加上侧面积即可
            R[u] = r, H[u] = h;     // 第 u 层的半径和高度确定
            dfs(u - 1, v + r * r * h, s + 2 * r * h + t);
        }
    
}

int main() {
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= m; i ++ ) {
        minv[i] += minv[i - 1] + i * i * i;         // r^2*h    r=h=1
        mins[i] += mins[i - 1] + 2 * i * i;         // 侧面积
    }

    // 设置两个哨兵,关于 r[u], h[u] 的范围限制时,需要用到 r[u+1],h[u+1] 来取 min,所以需要设置两个哨兵
    R[m + 1] = H[m + 1] = 1e9;

    // 从 m 层开始,当前体积、表面积都是 0
    dfs(m, 0, 0);

    if (res == 1e9) res = 0;                        // 无解情况
    printf("%d\\n", res);

    return 0;
}

以上是关于[dfs] aw168. 生日蛋糕(dfs剪枝与优化+分类讨论+思维+公式推导+数学+好题)的主要内容,如果未能解决你的问题,请参考以下文章

生日蛋糕(DFS)(搜索剪枝)生日蛋糕

POJ - 1190生日蛋糕 (dfs+剪枝)

生日蛋糕 (poj1190) (dfs剪枝)

[dfs] aw165. 小猫爬山(dfs剪枝与优化+好题)

[dfs] aw167. 木棒(dfs剪枝与优化+分类讨论+思维+好题)

[dfs] aw166. 数独(dfs剪枝与优化+状态压缩+代码技巧+好题)