01背包问题中遍历与循环顺序理解
问题说明
在01背包问题中,需要对背包和物品进行遍历。是先对背包进行遍历,还是先对物品进行遍历,亦或者二者都可以?那遍历的顺序是正序还是倒序呢,也就是说从前往后遍历还是从后往前遍历呢?
答案解释
01背包问题中有两种常见的解法,一种是一般的二维dp数组解法,另一种是状态压缩后的一维dp数组解法。针对不同的解法,上述问题的答案也不相同,下面分情况讨论。
二维dp数组
这种解法比较常规,也比较易于理解,具体的核心思路为:
定义dp[i][j]
,含义为前i
个物品在容量为j
的背包中的最大价值。dp[i][j]
的状态来源有两种:第一种是不放入第i
个物品,则说明前i-1
个物品已经达到了容量j
,因此dp[i][j]=dp[i-1][j]
;第二种是放入第i
个物品,则说明前i-1
个物品的容量为j-weight[i]
,因此dp[i][j]=dp[i-1][j-weight]+value[i]
;因为我们需要价值最大,所以dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight]+value[i])
。
j=0 | j=1 | j=2 | j=3 | j=4 | |
---|---|---|---|---|---|
i=0 | 0 | 0 | 0 | 0 | 0 |
i=1 | 0 | 5 | 5 | 5 | 5 |
i=2 | 0 | 5 | 11 | 16 | 16 |
i=3 | 0 | 5 | 11 | 16 | 20 |
我们根据迭代公式可以看到,求取dp[i][j]时我们需要的仅仅是第i-1行的0~ j列的数据。因此我们判断遍历顺序时,就需要看哪种顺序可以在求取dp[i][j]时其第i-1行的0~j列已经求完。我们发现无论是先遍历物品(按行求取),还是先遍历背包容量(按列求取),都可以满足上述要求,同时正序和倒序都满足上述要求。
所以针对这种解法,任何顺序都可以。
一维dp数组
一维dp数组就是把二维数组而状态压缩,定义dp[j]
,迭代公式为dp[j]=max(dp[j],dp[j-weight[i]]+value[i])
。
相对于求取dp[i][j]
后把dp[i-1][j]
的位置给替换了,这样相当于节省了空间。但是求取dp[i][j]
时需要用到第i-1行的0~j列,如果正序求取的话,第i-1行的0 ~ j列(左上方)的数据就已经被替换了,所以求解错误,如果倒序求取dp[i][j]
的话,第i-1行的0 ~ j列(左上方)的数据还没有被替换,因此求解正确。
知道了需要倒序求解后,我们再看是先遍历物品还是先遍历背包容量,如果先遍历背包容量(按列求取),且需要倒序,我们发现求取dp[i][j]
时,其第i-1行的0 ~ j列(左上方)的数据还没有被求取,因此不能先遍历背包容量。先遍历物品(按行求取)时,即使是倒序,第i-1行的0 ~ j列(左上方)的数据也已经被求取,因此我们需要先遍历物品,且需要是倒序!
总结
当我们采用状态压缩的方法来求解背包问题时,可以列出上述表格,按照以上分析思路来观察求解顺序,从而判断遍历与循环顺序。