POJ1190 生日蛋糕 DFS

题目链接

http://poj.org/problem?id=1190

分析

从下往上搜索,对于每一层,尝试其半径和高度,记录当前体积和表面积,上表面面积可在最底层直接加上,每次搜索只需要再加上增加的侧面积即可。

计算出半径和高度所在的合法区间,倒序枚举,减少分支。

预处理出从每一层开始,最少要增加的体积和表面积,进行常规的可行性剪枝和最优性剪枝。

假设当前搜索到第 d d d 层, h h h 表示高度, r r r 表示半径;

剩余 d − 1 d - 1 d1 层的体积为 N − v = ∑ k = 1 d − 1 h [ k ] ∗ r [ k ] 2 N - v = \sum_{k = 1}^{d - 1} h[k] * {r[k]}^2 Nv=k=1d1h[k]r[k]2

增加的表面积为 2 ∗ ∑ k = 1 d − 1 h [ k ] ∗ r [ k ] = 2 r [ d ] ∗ ∑ k = 1 d − 1 h [ k ] ∗ r [ k ] ∗ r [ d ] > 2 r [ d ] ∗ ∑ k = 1 d − 1 h [ k ] ∗ r [ k ] 2 = 2 ∗ ( N − v ) r [ d ] 2 * \sum_{k = 1}^{d - 1} h[k] * r[k] = {2 \over r[d]} * \sum_{k = 1}^{d - 1} h[k] * r[k] * r[d] > {2 \over r[d]} * \sum_{k = 1}^{d - 1} h[k] * {r[k]}^2 = {2 * (N - v) \over r[d]} 2k=1d1h[k]r[k]=r[d]2k=1d1h[k]r[k]r[d]>r[d]2k=1d1h[k]r[k]2=r[d]2(Nv)

因此还可以用当前表面积加上 2 ∗ ( N − v ) r [ d ] 2 * (N - v) \over r[d] r[d]2(Nv) 来进行最优性剪枝。

AC代码

#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

inline int read() {
	int num = 0;
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	while (c >= '0' && c <= '9')
		num = num * 10 + c - '0',c = getchar();
	return num;
}

const int maxm = 25, inf = 0x7fffffff;

int n, m, ans = inf, v, s;
int minv[maxm], mins[maxm], r[maxm], h[maxm];

void dfs(int d) {
	if (!d) {
		if (v == n) ans = min(ans, s);
		return;
	}
	if (v + minv[d] > n) return;
	if (s + mins[d] > ans) return;
	for (int i = min((int)sqrt(n - v), r[d + 1] - 1); i >= d; --i)
		for (int j = min((n - v) / (i * i), h[d + 1] - 1); j >= d; --j) {
			if (s + 2 * (n - v) / i > ans) return;
			r[d] = i, h[d] = j;
			v += i * i * j;
			if (d == m) s += i * i;
			s += 2 * i * j;
			dfs(d - 1);
			v -= i * i * j;
			if (d == m) s -= i * i;
			s -= 2 * i * j;
		}
}

int main() {
	n = read(), m = read();
	for (int i = 1; i <= m; ++i) {
		minv[i] = minv[i - 1] + i * i * i;
		mins[i] = mins[i - 1] + 2 * i * i;
	}
	r[m + 1] = h[m + 1] = inf;
	dfs(m);
	if (ans < inf) printf("%d", ans);
	else printf("0");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值