一、内容

生日蛋糕 POJ - 1190_最优性

二、思路

  • 三重剪枝

  • 第一重:压缩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;
}