一、内容

洛谷 P1731 生日蛋糕  dfs + 剪枝_搜索

二、思路



三重剪枝



第一重:压缩H,R的区间
由于H,R必须是整数,所以我们可知第一层的最小R为1,第二层为R,第i层为(m - i + 1),所以当i > 1,R的区间等于【m - i + 1, Ri-1 - 1】,R1的最大值为sqrt(n),因为当H1为1的时候
同理: Hi的范围【m - i + 1, H~i - 1~ - 1】, H1的最大值为n



第二重:可行性剪枝
v是当前已经得到的体积, 若v+minV(后面层数的最少体积) > V, 那么代表后面搜索可以直接删除。
用vMin数组保存还剩h层时最少的体积和。倒数第一层R,H为1,倒数第二层R,H为2,所以 j3 求个和



第三重:最优化剪枝
如果已经搜到了一个答案ans, 而当前搜到的体积为v,面积为s,那么若s + x >= ans,那么也不用再搜索了,这就是最优性剪枝。 那么如何确定x?
x = 2 ∑ j = i m \sum _{j=i}^{m} j=im Rj * Hj = 2/Ri ∑ j = i m \sum _{j=i}^{m} j=im Ri * Rj * Hj 由于Ri>= Rj
所以x >= 2/Ri ∑ j = i m \sum _{j=i}^{m} j=im *Rj * Rj * Hj = 2(V - v) / Ri
故若 s(当前已经搜出的面积) + 2 (V - v) / Ri >= ans 那么可以直接不用搜索了。



最后解释一下为什么从R,H的最大值开始搜:
S = R1 + 2 ∑ j = 1 m \sum _{j=1}^{m} j=1m Rj * Hj
V = ∑ j = 0 m \sum _{j=0}^{m} j=0m Rj2 * Hj s 所以 V / Rj = ∑ j = 0 m \sum _{j=0}^{m} j=0m Rj * Hj
所以当R越大时,S表面积就越小,所以从R,H的最大值开始枚举



三、代码

#include <cstdio>
#include <cmath>
#define min(a, b) ((a) > (b) ? (b) : (a))
int n, m, ans = 0x3f3f3f3f, vMin[16];
void init() {
//求出后面几层最少的体积
for (int i = 1; i <= 15; i++) {
vMin[i] = vMin[i - 1] + i * i * i;
}
}
void dfs(int h, int s, int v, int lastR, int lastH) {
if (h == 0) {
if (v == n) {
ans = min(s, ans);
}
return;
}
//可行性剪枝
if (v + vMin[h] > n) return;
//最优性剪枝
if (s + 2 *(n - v) / lastR >= ans) return;

for (int i = lastR - 1; i >= h; i--) {
for (int j = lastH - 1; j >= h; j--) {
if (h == m) {
//代表是第一层要多一个 Ri*Ri
dfs(h - 1, s + i * i + 2 * i *j, v + i * i * j, i, j);
} else {
dfs(h - 1, s + 2 * i * j, v + i * i * j, i, j);
}
}
}
}

int main() {
scanf("%d%d", &n, &m);
init();
dfs(m, 0, 0, sqrt(n) + 1, n + 1);//从还剩m层 面积为0 体积为0
ans = ans == 0x3f3f3f3f ? -1 : ans;
printf("%d", ans);
return 0;
}