POJ3124.The Bookcase(题解)
思路:我们发现一共需要求两维东西,一个是宽度,另一个是高度,那么依据我们的经验,可以将高度首先进行一次排序。那么是由高到低排好,还是由低到高排好呢?
- 考虑由低到高,显然每一次加入一个东西\(i\)时,该东西一定是最高的,该东西加入的那一排,高度一定是\(h[i]\),但是有一个问题是我们还需要知道其他两维的高度,那么我们必须再开两维数组来表示,显然解决了一部分问题,但是并不能完全解决问题。
- 考虑由高到低,由高到低一个最大的好处就是,这三排中的\(Max_high\)一定在头部,此时我们如果用数组来表示宽度的话,那么如果宽度不为0,就代表这一排前面一定有比这个高的,高度不会改变,所以我们能够用一维来表示两维能表示的东西,很好的解决了这个问题。
因为我们需要求最小面积 = \(\sum Max\_high * \sum Max\_width\),又发现了宽度可以继续表示高度,那么我们就设\(f(i,j,k)\)代表当前进行到第\(i\)个物品,且第一排第二排宽度分别为\(j,k\)时最小高度,状态转移方程:
\(f[i,j,k] = min(f[i-1,j,k] + (sum\_now - j - k == 0?h[i]:0),f[i-1,j-w[i],k] + (j-w[i] == 0?h[i]:0),f[i-1,j,k-w[i]] + (k-w[i] == 0?h[i]:0))\)
最后扫描一遍原数组得到答案。
这个题我们需要做到两维才能够AC,发现转移方程中\(i\)是由\(i-1\)转移,那么一个显而易见的想法是开滚动数组(然而我还是过不了,因此后两维枚举时倒着枚举,这样就能够AC了。详情见代码:
\(Code:\)
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define ch() getchar()
#define pc(x) putchar(x)
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define bep(i, a, b) for (int i = a; i >= b; --i)
#define lowbit(x) x &(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template <typename T>
void read(T &x) {
static char c;
static int f;
for (c = ch(), f = 1; c < '0' || c > '9'; c = ch())
if (c == '-') f = -f;
for (x = 0; c >= '0' && c <= '9'; c = ch()) x = x * 10 + (c & 15);
x *= f;
}
template <typename T>
void write(T x) {
static char q[65];
int cnt = 0;
if (x < 0) pc('-'), x = -x;
q[++cnt] = x % 10, x /= 10;
while (x) q[++cnt] = x % 10, x /= 10;
while (cnt) pc(q[cnt--] + '0');
}
const int N = 2130;
int _, n;
int f[N][N];
struct node {
int h, t;
bool operator<(const node &a) const { return a.h < h; }
} a[N];
void solve() {
read(_); // memset(f,0x3f,sizeof f);
while (_--) {
read(n);
int sum = 0;
// memset(f, 0x3f, sizeof f);
int sums = 0;
f[0][0] = 0;
rep(i, 1, n) {
int x, y;
read(x);
read(y);
sums += y;
a[i].h = x;
a[i].t = y;
}
memset(f,0x3f,sizeof f);
f[0][0] = 0;
sort(a + 1, a + 1 + n);
int ans = 99999999;
rep(i, 2, n) { // printf("%d\n",a[i].h);
//sum += a[i].t;
bep(j, sums, 0) {
for (int k = sums; k >= 0; --k) {
//int &res = f[j][k];
if(f[j][k] == 0x3f3f3f3f or j+k > sums)continue;
f[j + a[i].t][k] = min(f[j + a[i].t][k],
f[j][k] + (j == 0 ? a[i].h : 0));
f[j][k + a[i].t] = min(
f[j][k + a[i].t], f[j][k] + (k == 0 ? a[i].h : 0));
//res = 0x3f3f3f3f;
// if (j - a[i].t >= 0)
// res = min(
// res, f[j - a[i].t][k] + (j == a[i].t ? a[i].h : 0));
// if (k - a[i].t >= 0)
// res = min(
// res, f[j][k - a[i].t] + (k == a[i].t ? a[i].h : 0));
// res = min(res,
// f[j][k] + (sum - (j + k) == a[i].t ? a[i].h : 0));
}
}
sum += a[i].t;
}
rep(i, 1, sums) {
rep(j, 1, sums) {
if (f[i][j] >= 0x3f3f3f3f or i + j >= sums) continue;
// printf("{%d %d %d} %d %d %d\n",i,j,sum-i-j,f[n][i][j],
// max({i,j,sum-i-j}),f[n][i][j] * max({i,j,sum-i-j}));
ans = min((f[i][j] + a[1].h) * max(max(i, j), sums - i - j), ans);
// f[0][i][j] = f[1][i][j] = 0x3f3f3f3f;
}
}
write(ans);
pc('\n');
}
}
signed main(int argc, char const *argv[]) {
solve();
return 0;
}