背包 dp 一些小 trick 的记录

前言

学习背包 dp,遇到了一些觉得有典型性的一些题目,故记录在此,方便以后查看。

如果以后发现有价值的会更新。

P1417 烹调方案

01 背包。

特殊点:物品的价值在动态变化,物品 \(1\) 在物品 \(2\) 前先做不一定更优,即无后效性。

但由于价值的变化满足式子 \(a_i - t \times b_i\),那么对于任意的 \(\forall t\in [1,T]\),物品 \(i\) 想要比物品 \(j\) 先做,应当满足:

\[a_i-t\times b_i+a_j-(t+c_i)\times b_j > a_j-t\times b_j+a_i-(t+c_j)\times b_i \]

化简可得:

\[\dfrac{c_i}{b_i} < \dfrac{c_j}{b_j} \]

因此按以上式子对物品排序再进行 dp 即可。

P6771 太空电梯

多重背包,观察数据范围可知不需要使用单调队列或二进制拆分优化。

特殊点:物品有着自己独有的一个体积,如果不考虑顺序直接 dp,会导致出现不同方案尽管选择的方块一样但是由于高度限制导致有些合法有些不合法。

因此一个显然的贪心是将物品按照 \(a_i\) 排序再进行 dp。

P2979 [USACO10JAN] Cheese Towers S

完全背包。

特殊点:部分物体放在最后时可以减少以前物品的体积。很显然,由于减少的体积不可叠加,因此我们没必要为了减少体积放多个大奶酪。

由于选择压在上方的大奶酪是完全独立于整个过程的,因此我们可以先不考虑大奶酪计算 \([0,1.25\times T]\) 的最优解,再枚举大奶酪计算在上面压一块大奶酪的最优解。

P1858 多人背包

01 背包求前 \(k\) 优解。(\(k \le 50\))

方案:由于 \(k\) 比较小,我们可以直接加一个维度,表示当前阶段当前状态下的第 \(k\) 优解。

那么状态转移方程可以写为:(为方便理解未进行降维优化)

\[f_{i,j,k}\gets \max(f_{i-1,j,x},f_{i-1,j-w_i,y}+v_i) \]

其中的 \(x,y\) 表示曾经阶段曾经状态的第 \(x,y\) 优解,如果我们暴力枚举肯定超时,但是很显然的是:

  • 最优解肯定由 \(f_{i-1,j}\) 的最优解和 \(f_{i-1,j-w_i}+v_i\) 的最优解推出。
  • 如果最优解由 \(f_{i-1,j}\) 的最优解推出,那么次优解由 \(f_{i-1,j}\) 的次优解和 \(f_{i-1,j-w_i}+v_i\) 的最优解推出;
  • 如果最优解由 \(f_{i-1,j-w_i}+v_i\) 的最优解推出,那么次优解由 \(f_{i-1,j}\) 的最优解和 \(f_{i-1,j-w_i}+v_i\) 的次优解推出。
  • 所以 \(f_{i-1,j}\)\(k\) 优解如果由 \(f_{i-1,j}\)\(x\) 优解推出,则 \(k+1\) 优解由 \(f_{i-1,j}\)\(x+1\) 优解和 \(f_{i-1,j-w_i}+v_i\)\(y\) 优解推出;反之亦然。

因此我们便可以书写以下代码(进行降维优化):

for (int i = 1; i <= n; i++) {
	for (int j = V; j >= w[i]; j--) {
		for (int t = 1,x = 1,y = 1; t <= k; t++) {
			int t1 = f[j][x],t2 = f[j-w[i]][y]+v[i];
			if (t1 <= t2) {
				tmp[t] = f[j-w[i]][y++]+v[i];
			}else{
				tmp[t] = f[j][x++];
			}
		}
		for (int t = 1; t <= k; t++) {
			f[j][t] = tmp[t];
		}
	}
}

注意由于 \(f\) 数组的任意优解必须要由上一阶段的推出,因此本阶段的值应当先由临时数组存好,防止本阶段使用本阶段代码导致错误。

posted @ 2024-12-11 19:41  Ascnbeta  阅读(27)  评论(0)    收藏  举报