[dfs] aw168. 生日蛋糕(dfs剪枝与优化+分类讨论+思维+公式推导+数学+好题)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[dfs] aw168. 生日蛋糕(dfs剪枝与优化+分类讨论+思维+公式推导+数学+好题)相关的知识,希望对你有一定的参考价值。
1. 题目来源
链接:168. 生日蛋糕
2. 题目解析
非常非常经典的题目,优化很难…
首先的首先,理解题意,问题抽象。在总体积为 n
,总层数为 m
的情况下确定每层的 r
和 h
,使得要去的蛋糕外表面积最小。
首先确定枚举顺序,枚举每层情况,再枚举每层的 r
和 h
,保证 r
、h
是递增的整数。枚举的种类是指数级别的,需要加入大量剪枝才可能通过本题。
优化:
- 优化搜索顺序:从底向上枚举蛋糕每一层,因为底层占用体积大,后续分支少。每层中先枚举
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] aw165. 小猫爬山(dfs剪枝与优化+好题)