Nine lectures on Knapsack Problems

本文深入解析01背包问题的基础概念,包括基本问题描述、状态转移方程和优化空间复杂度的方法。接着介绍了完全背包问题,并讨论了两种问法的初始化策略。核心算法和伪代码演示了如何用一维数组高效解决01背包问题,以及如何根据问题需求调整初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Knapsack Problems-背包九讲

01背包问题

题目描述:有N件物品和一个容量为V的背包。放入第i件物品耗费的空间是 C i C_i Ci,得到的价值是 W i W_i Wi。求解将哪些物品装入背包可使价值总和最大。

算法思路:这是最基础的背包问题,特点是每种物品仅有一件,可以选择放或不放。用子问题定义状态,即F[i,v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:F[i,v]=max{F[i−1,v],F[i−1,v− C i C_i Ci]+ W i W_i Wi}。这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下,“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只和前i−1件物品相关的问题。如果不放第i件物品,那么问题就转化为“前i−1件物品放入容量为v的背包中”,价值为F[i−1,v];如果放第i件物品,那么问题就转化为“前i−1件物品放入剩下的容量为v− C i C_i Ci的背包中”,此时能获得的最大价值就是F[i−1,v− C i C_i Ci]再加上通过放入第i件物品获得的价值 W i W_i Wi

伪代码如下,

F[0,0…V]=0
for i = 1 to N
for v = C i C_i Ci to V
F[i,v]=max{F[i-1,v],F[i-1,v- C i C_i Ci]+ W i W_i Wi}

以上方法的时间和空间复杂度均为O(V N),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以优化到O(V)。先考虑上面讲的基本思路如何实现,肯定是有一个主循环i= 1…N,每次算出来的二维数组F[i,0…V]的所有值。那么,如果只用一个数组F[0…V],能不能保证第i次循环结束后F[v]中表示的就是我们定义的状态F[i,v]呢?F[i,v]是由F[i−1,v]和F[i−1,v− C i C_i Ci]两个子问题递推而来,能否保证在推F[i,v]时(也即在第i次主循环中推F[v]时)能够取用F[i−1,v]和F[i−1,v−Ci]的值呢?事实上,这要求在每次主循环中我们以v=V…0的递减顺序计算F[v],这样才能保证推F[v]时F[v− C i C_i Ci]保存的是状态F[i−1,v− C i C_i Ci]的值。

伪代码如下,

F[0…V]=0
for i = 1 to N
for v = V to C i C_i Ci
F[v]=max{F[v],F[v- C i C_i Ci]+ W i W_i Wi}

其中的F[v]=max{F[v],F[v− C i C_i Ci]+ W i W_i Wi}一句,恰就对应于我们原来的转移方程,因为现在的F[v−Ci]就相当于原来的F[i−1,v− C i C_i Ci]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了F[i,v]由F[i,v− C i C_i Ci]推导得到,与本题意不符。事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明。

def ZeroOnePack(F, C, W)

​ for v = V to C

​ F[v]=max(F[v],f[v−C]+W)

有了这个过程以后,01背包问题的伪代码就可以这样写:

for i = 1 to N

​ ZeroOnePack(F, C i C_i Ci, W i W_i Wi)

初始化的细节问题。我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。

如果是第一种问法,要求恰好装满背包,那么在初始化时除了F[0]为0,其它F[1…V]均设为−∞,这样就可以保证最终得到的F[V]是一种恰好装满背包的最优解。如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将F[0…V]全部设为0。这是为什么呢?可以这样理解,初始化的F数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可以在什么也不装且价值为0的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。这个小技巧完全可以推广到其它类型的背包问题,后面也就不再对进行状态转移之前的初始化进行讲解。

完全背包问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JIeJaitt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值