题目链接
http://poj.org/problem?id=1190
分析
从下往上搜索,对于每一层,尝试其半径和高度,记录当前体积和表面积,上表面面积可在最底层直接加上,每次搜索只需要再加上增加的侧面积即可。
计算出半径和高度所在的合法区间,倒序枚举,减少分支。
预处理出从每一层开始,最少要增加的体积和表面积,进行常规的可行性剪枝和最优性剪枝。
假设当前搜索到第 d d d 层, h h h 表示高度, r r r 表示半径;
剩余 d − 1 d - 1 d−1 层的体积为 N − v = ∑ k = 1 d − 1 h [ k ] ∗ r [ k ] 2 N - v = \sum_{k = 1}^{d - 1} h[k] * {r[k]}^2 N−v=∑k=1d−1h[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]} 2∗∑k=1d−1h[k]∗r[k]=r[d]2∗∑k=1d−1h[k]∗r[k]∗r[d]>r[d]2∗∑k=1d−1h[k]∗r[k]2=r[d]2∗(N−v);
因此还可以用当前表面积加上 2 ∗ ( N − v ) r [ d ] 2 * (N - v) \over r[d] r[d]2∗(N−v) 来进行最优性剪枝。
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;
}