目录
- 01背包问题
- 完全背包问题
- 多重背包问题
- 混合三种背包问题
- 二维费用的背包问题
- 分组的背包问题
- 有依赖的背包问题
- 泛化物品
- 背包问题问法的变化
01背包问题
基本问题
有 (N) 件物品和一个容量为 (V) 的背包。第 (i) 件物品的费用是 (c[i]),价值是 (w[i])。求解将哪些物品装入背包可使价值总和最大。
基本思路
(f[i][v]) 表示前 (i) 件物品恰放入一个容量为 (v) 的背包可以获得的最大价值。则其状态转移方程便是:
(f[N][V]) 则为背包可能的最大价值。
优化空间复杂度
时间复杂度为(O(N imes V)),空间复杂度可以优化为(O(V))。
for i = 1..N
for v = V..0
f[v] = max{f[v], f[v - c[i]] + w[i]};
初始化的细节问题
- 要求恰好装满背包:(f[0]) 设置为0,其他(f[1..V]) 均设置为 (- infty)。
- 不要求恰好装满背包,只要求价值最大:(f[0..V])均设置为0。
题目
完全背包问题
基本问题
有 (N) 种物品和一个容量为 (V) 的背包,每种物品都有无限件可用。第 (i) 种物品的费用是 (c[i]),价值是(w[i])。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
基本思路
令 (f[i][v]) 表示前 (i) 种物品恰放入一个容量为 (v) 的背包的最大权值。可以得到状态转移方程式:
改进做法
我们可以通过一个 (O(N imes V)) 的方法进行完全背包问题求解。状态转移方程式为:
(f[V]) 则为背包可能的最大价值。
for i = 1..N
for v = 0..V
f[v] = max{f[v], f[v - cost] + weight}
题目
多重背包问题
基本问题
有 (N) 种物品和一个容量为 (V) 的背包。第 (i) 种物品最多有 (n[i])件可用,每件费用是 (c[i]),价值是 (w[i])。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
基本思路
令 (f[i][v]) 表示前 (i) 种物品恰放入一个容量为 (v) 的背包的最大权值,则有状态转移方程:
复杂度为 (O(V imes sum_i n[i]))。
二进制优化
将第 (i) 种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 (1,2,4,...,2^{k-1}, n[i] - 2^{k+1}),且 (k) 是满足 (n[i] - 2^{k+1} > 0) 的最大整数。例如:如果 (n[i]) 为 (13),就将这种物品分成系数分别为 (1,2,4,6) 的四件物品。
这样就将第 (i) 种物品分成了 (O(log n[i])) 种物品,将原问题转化为了复杂度为 (O(V imes sum log n[i])) 的 01 背包问题 。
procedure ZeroOnePack(cost, weight)
for v = V..cost
f[v] = max{f[v], f[v - cost] + weight}
procedure CompletePack(cost, weight)
for v = cost..V
f[v] = max{f[v], f[v - cost] + weight}
procedure MultiplePack(cost, weight, amount)
if cost * amount >= V {
CompletePack(cost, weight)
Return
}
integer k = 1
while k < amount {
ZeroOnePack(k * cost, k * weight)
amount = amount - k
k = k * 2
}
ZeroOnePack(amount * cost, amount * weight)
单调队列优化
基于基本算法的状态转移方程,应用单调队列的方法使每个状态的值可以以均摊 (O(1)) 的时间求解。总的复杂度可以达到 (O(NV))
题目
混合三种背包问题
基本问题
有的物品只可以取一次(01 背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。即将前三种情况混合起来。求解背包可能的最大价值。
基本思路
混合使用以上的三种背包问题求解方法。
for i = 1..N
if 第 i 件物品是 01 背包
ZeroOnePack(c[i], w[i])
else if 第 i 件物品是完全背包
CompletePack(c[i], w[i])
else if 第 i 件物品是多重背包
MultiplePack(c[i], w[i], n[i])
二维费用的背包问题
基本问题
对于每件物品,具有两种不同的费用;选择这件物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择物品可以得到最大的价值。设这两种代价分别为 代价 1 和 代价 2 ,第 (i) 件物品所需的两种代价分别为 (a[i]) 和 (b[i])。两种代价可付出的最大值(两种背包容量)分别为 (V) 和 (U)。物品的价值为 (w[i])。
基本思路
设 (f[i][v][u]) 表示前 (i) 件物品付出两种代价分别为 (v) 和 (u) 时可获得的最大价值。状态转移方程为:
物品个数限制
有时,“二维费用”的条件是以这样一种隐含的方式给出的:最多只能取 (M) 件物品。这事实上相当于每件物品多了一种“件数”的费用,每个物品的件数费用均为 (1),可以付出的最大件数费用为 (M)。
设 (f[v][m]) 表示付出费用 (v)、最多选 (m) 件时可得到的最大价值,则根据物品的类型(01、完全、多重)用不同的方法循环更新,最后在 (f[0..V][0..M]) 范围内寻找答案
分组的背包问题
基本问题
有 (N) 件物品和一个容量为 (V) 的背包。第 (i) 件物品的费用是 (c[i]),价值是 (w[i])。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
基本思路
(f[k][v]) 表示前 (k) 组物品花费费用 (v) 能取得的最大权值,则有:
伪代码如下。注意三层 for 循环的顺序不能写错。
for 所有的组 k
for v = V..0
for 所有的 i 属于组 k
f[v] = max{f[v], f[v - c[i]] + w[i]}
有依赖的背包问题
基本问题
这种背包问题的物品间存在某种 依赖 的关系。也就是说,(i) 依赖于 (j),表示
若选物品 (i),则必须选物品 (j)。为了简化起见,我们先设没有某个物品既依赖于
别的物品,又被别的物品所依赖;另外,没有某件物品同时依赖多件物品。
基本思路
我们可以对主件 (i) 的“附件集合”先进行一次 01 背包,得到费用依次为 (0..V-c[i])所有这些值时相应的最大价值 (f'[0..V-c[i]])。那么这个主件及它的附件集合相当于 (V-c[i]+1) 个物品的物品组,其中费用为 (c[i]+k) 的物品的价值为 (f'[k]+w[i])。也就是说原来指数级的策略中有很多策略都是冗余的,通过一次 01 背包后,将主件 (i) 转化为 (V-c[i]+1) 个物品的物品组,就可以直接应用分组背包问题 =的算法解决问题了。
泛化物品
泛化物品的定义
考虑这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它
的费用而变化。这就是泛化物品的概念。
更严格的定义是:在背包容量为 (V) 的背包问题中,泛化物品是一个定义域为 (0..V)
中的整数的函数 (h),当分配给它的费用为 (v) 时,能得到的价值就是 (h(v))。