背包问题的倒序枚举与正序枚举

版权声明:欢迎转载ヽ(•ω•ゞ)···如有表意不清或您没有看懂评论即可! https://blog.csdn.net/yandaoqiusheng/article/details/84929357

这可能是困扰很多人很长时间的问题吧。

先把各个变量列出来

体积为 V V 的背包,有 n n 个物品,每个物品的体积为 w i w_i ,价值为 v i v_i ,每个物品装一次,求最大价值

来这看的肯定都是学习过基础背包的人,如果没有可以看我另一篇博客,里面有详细解释——点这里
下面先给出二维的转移方程

f [ i ] [ j ] = m a x ( f [ i 1 ] [ j ] , f [ i 1 ] [ j w [ i ] ] + v [ i ] ) f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i])

首先,对于二维数组的背包来说,正序和逆序是无所谓的,因为你把状态都保存了下来,而一位数组的背包是会覆盖之前的状态的

要想知道 f [ i ] [ j ] f[i][j] ,你要从 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 [ j ] = m a x ( f [ j ] , f [ j w [ i ] ] + v [ i ] ) f[j] = max(f[j], f[j - w[i]] + v[i])

f [ j ] f[j] 表示在执行 i i 次循环后(此时已经处理 i i 个物品),前 i i 个物体放到容量 j j 的背包时的最大价值,即之前的 f [ i ] [ j ] f[i][j] 。与二维相比较,它把第一维压去了,但是二者表达的含义是相同的,只不过 f [ j ] f[j] 一直在重复使用,所以,也会出现第 i i 次循环覆盖第 i 1 i-1 次循环的结果。

按方程来说,其中有许多对应相等的关系,比如 f [ i 1 ] [ j ] f[i - 1][j] f [ j ] f[j] 就是相等的,这是为什么呢?求一下这几个值就好了

  1. i 1 i-1 个物品放到容量 j j 的背包中带来的收益( f [ i 1 ] [ j ] f[i-1][j]
    由于在执行第 i i 次循环时, f [ j ] f[j] 存储的是前 i i 个物体放到容量为 j j 的背包时的最大价值,在求前 i i 个物体放到容量 j j 时的最大价值(即之前的 f [ i ] [ j ] f[i][j] )时,我们正在执行第 i i 次循环, f [ v ] f[v] 的值还是在第 i 1 i-1 次循环时存下的值,在此时取出的 f [ j ] f[j ] 就是前 i 1 i-1 个物体放到容量 j j 的背包时的最大价值,即 f [ i 1 ] [ j ] f[i-1][j]
  2. i 1 i-1 件物品放到容量为 j w [ i ] j-w[i] 的背包中带来的收益( f [ i 1 ] [ j w [ i ] ] + v [ i ] f[i-1][j-w[i]]+v[i]
    由于在执行第 i i 次循环前, f [ 0... V ] f[0...V] 中保存的是第 i 1 i-1 次循环的结果,即是前 i 1 i-1 个物体分别放到容量 0... V 0...V 时的最大价值,即 f [ i 1 ] [ 0... V ] f[i-1][0...V] ,则在执行第 i i 次循环前, f f 数组中 j w [ i ] j-w[i] 的位置存储就是我们要找的前 i 1 i-1 件物品放到容量为 j w [ i ] j-w[i] 的背包中带来的收益 (即之前的 f [ i 1 ] [ j w [ i ] ] f[i-1][j-w[i]] )。

具体来说,由于在执行 j j 时,是还没执行到 j w [ i ] j-w[i] 的,因此, f [ j w [ i ] ] f[j-w[i]] 保存的还是第 i 1 i-1 次循环的结果。即在执行第 i i 次循环且背包容量为 j j 时,此时的 f [ j ] f[j] 存储的是 f [ i 1 ] [ j ] f[i-1][j] ,此时 f [ j w [ i ] ] f[j-w[i]] 存储的是 f [ i 1 ] [ j w [ i ] ] f[i-1][j-w[i]]

扫描二维码关注公众号,回复: 4486818 查看本文章

相反,如果在执行第 i i 次循环时,背包容量按照 0... V 0...V 的顺序遍历一遍,来检测第 i i 件物品是否能放。此时在执行第 i i 次循环且背包容量为 j j 时,此时的 f [ j ] f[j] 存储的是 f [ i 1 ] [ j ] f[i-1][j] ,但是,此时 f [ j w [ i ] ] f[j-w[i]] 存储的是 f [ i ] [ j w [ i ] ] f[i][j-w[i]]

因为 j > j w [ i ] j>j-w[i] ,所以第 i i 次循环中,执行背包容量为 j j 时,容量为 j w [ i ] j-w[i] 的背包已经计算过了,即 f [ j w [ i ] ] f[j-w[i]] 中存储的是 f [ i ] [ j w [ i ] ] f[i][j-w[i]] 。它会从一开始就装入某个物品,只是为了价值最大,重复是肯定要存在的。即对于01背包,按照增序枚举背包容量是不对的。如下图

我们现在要求 i = 2 i=2 时的 f [ 5 ] f[5]: 橙色的为数组现在存储的值,这些值是 i=1 ( ) 时(上一次循环)存入数组 f 的。相当于 f [ i 1 ] [ j ] f[i-1][j]
而黄色的是我们要求的值,在求 f [ 5 ] f[5] 之前, f [ 5 ] = 5 f[5]=5 ,即 f [ i 1 ] [ 5 ] = 5 f[i-1][5]=5
现在要求 i = 2 i=2 时的 f [ 5 ] = f [ 5 2 ] + 10 = 5 + 10 = 15 > f [ i 1 ] [ 5 ] = 5 f[5]=f[5-2]+10=5+10=15>f[i-1][5]=5
f [ 5 ] = 15 f[5]=15
要注意在求 f [ j ] f[j] 时,它引用的 f [ j w [ i ] ] f[j-w[i]] f [ j ] f[j] 都是上一次循环的结果

我们现在要求 i = 2 i=2 时的 f [ 5 ] f[5]
橙色为数组现在存储的值,这些值是 i = 2 i=2 时(本次循环)存入数组 f f 的。相当于 f [ i ] [ j ] f[i][j] 。这是由于,我们是增序遍历数组 f f 的,在求 f [ j ] f[j] 时, j j 之前的值 ( 0... v 1 ) (0...v-1) 都已经在第 i i 次循环中求出。
黄色是我们要求的值,在求 f [ 5 ] f[5] 之前, f [ 5 ] = 5 f[5]=5 ,即 f [ i 1 ] [ 5 ] = 5 f[i-1][5]=5
现在要求 i = 2 i=2 时的 f [ 5 ] = f [ 5 2 ] + 10 = 10 + 10 = 20 > f [ i 1 ] [ 5 ] = 5 f[5]=f[5-2]+10=10+10=20>f[i-1][5]=5 ,故 f [ 5 ] = 20 f[5]=20
其中引用的 f [ 3 ] f[3] f [ i ] [ 3 ] f[i][3] 而不是 f [ i 1 ] [ 3 ] f[i-1][3]
注意一点,在求 f [ j ] f[j] 时,它引用的 f [ j w [ i ] ] f[j-w[i]] 是本次循环的结果,而 f [ j ] f[j] 是上一次循环的结果
I n In o t h e r other w o r d s words
在检测背包容量为 5 5 时,看物品 2 2 是否加入
由状态转移方程可知,我们的 f [ 5 ] f[5] 需要引用自己本身和 f [ 3 ] f[3]
由于背包容量为 3 3 时,可以装入物品 2 2 ,且收益比之前的大,所以放入背包了。
在检测 f [ 5 ] f[5] 时,肯定要加上物品 2 2 的收益,而 f [ 5 ] f[5] 在引用 f [ 3 ] f[3] 时, f [ 3 ] f[3] 时已经加过一次物品 2 2
因此,在枚举背包容量时,物品2加入了多次。
然后我们明确三个问题

  1. j w [ i ] < j j-w[i]<j
  2. 状态 f [ i ] [ j ] f[i][j] 是由 f [ i 1 ] [ j ] f[i-1][j] f [ i 1 ] [ j w [ i ] ] f[i-1][j-w[i]] 两个状态决定的
  3. 对于物品 i i ,我们在枚举背包容量时,只要背包容量能装下物品 i i 且收益比原来的大,就会成功放入物品 i i

具体来说,枚举背包容量时,是以递增的顺序的话,由于 j w [ i ] < v j-w[i]<v ,则会先计算 j w [ i ] j-w[i] 。在背包容量为 j w [ i ] j-w[i] 时,一旦装入了物品 i i ,由于求 f [ j ] f[j] 需要使用 f [ i 1 ] [ j w [ i ] ] f[i-1][j-w[i]] ,而若求 f [ j ] f[j] 时也可以装入物品 i i 的话,那么在背包容量为 v v 时,容量为 j j 的背包就装入可两次物品。又若 j w [ i ] j-w[i] 是由之前的状态推出,它们也成功装入物品i的话,那么容量为 j j 的背包就装入了多次物品 i i 了。
此时,在计算 f [ j ] f[j] 时,已经把物品 i i 能装入的全装入容量为 j j 的背包了,此时装入物品 i i 的次数一定是最大的
所以,顺序枚举容量是完全背包问题最简捷的解决方案。

图片来源及参考博客:
https://blog.csdn.net/c_circle/article/details/78804728
背包九讲

猜你喜欢

转载自blog.csdn.net/yandaoqiusheng/article/details/84929357