[luogu p2160] [SHOI2007]书柜的尺寸

P2160 [SHOI2007]书柜的尺寸 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

把书按高度从大到小排序,依次考虑放置,每一层第一个被放置的书的高度就是这一层的高度。

\(f(i, j, k, l)\) 表示考虑了前 \(i\) 本书,第一层宽度为 \(j\),第二层宽度为 \(k\),第三层宽度为 \(l\) 时,最小的整个书架的高度(紫题应该就在于这个状态比较难想)。

为什么会这么想,首先考虑 \(t_i\)\(h_i\) 范围很小,可以猜测是 dp 的某一维,而且发现如果设成讨论每个书被放在哪里,状态的量级会高达 \(3^{70}\),这是不合适的,而这 \(3^{70}\) 个状态中其实有大量状态的第一层,第二层,第三层宽度分别完全一致(因为三层所有宽度的情况最多只有 \(2100^3\) 种,远小于 \(3^{70}\)),这种情况下转移也是一致的,可以一起处理,那就直接按照三层的宽度情况分类即可(这是本题的思维难点)。

其次,如果考虑前 \(i\) 本书,第一层高度为 \(j\),第二层高度为 \(k\),第三层高度为 \(l\) 的最小化整个书架的宽度,会发现很难转移,因为根本没有利用好开头所说的那个结论。

想到更换转移,于是有了上面的状态设计,发现这样就容易转移了。

刷表转移,对于 \(p = f(i, j, k, l)\),我们考虑对第一层进行放书,第二第三层完全同理。

下一个状态是 \(f(i + 1, j + t_i, k, l)\)。如果 \(j = 0\),把它和 \(p + h_i\) 取一个最小值,代表放了第一层放下了第一本书,我们统计上第一层的高度;否则如果 \(j \ne 0\),代表第一层已经放过一本书了,直接把它和 \(p\) 取一个最小值——显然第一层的高度已经统计在里面了。

发现时空复杂度仍然危险,考虑优化。

其实很容易发现 \(i, j, k\) 确定了 \(l\) 也就确定了,具体来说前 \(i\) 本书都放在书架中,第三层的宽度就是前 \(i\) 本书的总宽度减去第一层的宽度 \(j\) 减去第二层的宽度 \(k\)。因此第四维可以删掉。

然后这个题第一维还需要一个滚动。

统计答案就是直接大力统计最后状态。

时间复杂度 \(\mathcal{O}(n^3t^2)\)\(t\) 表示 \(t_i\) 的数量级。

这个题是 SHOI 2007,可以看到这题当时时限开了 5s,但是现在 1s 就可过,时代的进步~

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-09-23 21:02:08 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-09-23 22:35:56
 */
#include <bits/stdc++.h>

inline int read() {
	int x = 0;
	bool flag = true;
	char ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-')
			flag = false;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = (x << 1) + (x << 3) + ch - '0';
		ch = getchar();
	}
	if(flag)
		return x;
	return ~(x - 1);
}

const int maxn = 75;
const int maxm = maxn * 35;

int f[2][maxm][maxm];

struct node {
	int h, t;
}a[maxn];

inline bool getmin(int &a, int b) {
	return b < a ? a = b, true : false;
}

inline bool getmin(long long &a, long long b) {
	return b < a ? a = b, true : false;
}

int main() {
	int n = read(); 
	for (int i = 1; i <= n; ++i) {
		int h = read(), t = read();
		a[i] = (node){h, t};
	}

	std :: sort(a + 1, a + 1 + n, [](node b, node c) {
		return b.h > c.h;
	});
	
	std :: memset(f[0], 0x3f, sizeof(f[0]));
	f[0][0][0] = 0;

	int x = 0;
	for (int u = 1; u <= n; x += a[u].t, ++u) {
		int i = u & 1, t = a[u].t, h = a[u].h;
		std :: memset(f[i], 0x3f, sizeof(f[i]));
		for (int j = 0; j <= x; ++j) {
			for (int k = 0; k <= x - j; ++k) {
				int p = f[i ^ 1][j][k];
				getmin(f[i][j + t][k], p + (j ? 0 : h));
				getmin(f[i][j][k + t], p + (k ? 0 : h));
				int l = x - j - k;
				getmin(f[i][j][k], p + (l ? 0 : h));
			}
		}
	}

	long long ans = LONG_LONG_MAX;
	for (int i = 1; i < x; ++i)
		for (int j = 1; j < x - i; ++j)
			getmin(ans, 1LL * std :: max({i, j, x - i - j}) * f[n & 1][i][j]);
	
	printf("%lld\n", ans);
	return 0;
}
posted @ 2022-09-28 20:02  dbxxx  阅读(104)  评论(0编辑  收藏  举报