老早之前就听说过的题。好像讲过不只一遍。不过才从bzoj上翻出来。
记得是神奇的dp优化。为啥这年头卡常数的题这么多。
首先肯定这玩意多项式可解。六维状态肯定能搞定。然后会mle+tle。
首先考虑从高到低放书,那么后面的如果不新开一行的话对高度没有影响。瞬间少了3维状态。
然后发现只要知道两维的总宽度和当前是第几位,就能知道第三维的宽度。于是一维2100缩70,开滚动可过。
当然还是需要一些常数优化的技艺。比如用宽度直接判断这一行有没有书。这样可以省下不少时间,而且好像只能这样才存得下。64MB太紧了点。另外把函数里的int改成const int&会变慢,不知为何。
居然花了这么久才搞出来。我太年轻了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct book {
int w, h;
};
inline bool cmpBook(const book& a, const book& b) {
return a. h > b. h;
}
const int maxn = 71;
const int maxw = 2109;
const int inf = 0x3f3f3f3f;
int n, f[2][maxw][maxw], sw;
book a[maxn];
inline void upmin(int& _a_, int _b_) {
if (_a_ > _b_)
_a_ = _b_;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
scanf("%d", &n);
sw = 0;
for (int i = 0; i < n; ++ i) {
scanf("%d%d", &a[i]. h, &a[i]. w);
sw += a[i]. w;
}
sort(a, a + n, cmpBook);
memset(f, 0x3f, sizeof(f));
int cur = 0, prv = 1;
f[cur][0][0] = a[0]. h;
for (int i = 1; i < n; ++ i) {
swap(cur, prv);
for (int j = 0; j <= sw; ++ j)
for (int k = 0; k <= sw; ++ k)
f[cur][j][k] = inf;
for (int j = 0; j <= sw; ++ j)
for (int k = 0; k <= sw; ++ k)
if (f[prv][j][k] < inf) {
upmin(f[cur][j][k], f[prv][j][k]);
if (k) {
upmin(f[cur][j][k + a[i]. w], f[prv][j][k]);
if (j)
upmin(f[cur][j + a[i]. w][k], f[prv][j][k]);
else
upmin(f[cur][j + a[i]. w][k], f[prv][j][k] + a[i]. h);
}
else
upmin(f[cur][j][k + a[i]. w], f[prv][j][k] + a[i]. h);
}
}
int ans = inf;
for (int j = 1; j <= sw; ++ j)
for (int k = 1; k <= sw; ++ k)
if (sw - j - k && f[cur][j][k] < inf)
upmin(ans, f[cur][j][k] * max(max(j, k), sw - j - k));
printf("%d\n", ans);
}