背包问题
-
背包有最大承重,商店中的每种物品都有价值与重量两个属性,基于此提出各种问题:装下最多重量物品、装下最大总价值物品、有多少种方式正好带走满满一书包物品等等。
-
Backpack,最简单的题型是背包中的物品能不能拼出重量target,求出最大的target,背包问题中,数组大小与总承重有关,背包问题要把总承重放入状态!这是很特殊的一个地方。要求前N个物品能不能拼出重量0,1,...,M,子问题是需要知道前N-1个物品能不能拼出重量0,1,...,M,由此得出状态f[i][w] = 能否用前i个物品拼出重量w(true/false)。转移方程如下:
-
Backpack5,题目改成有多少种方式拼出target,要求前N个物品有多少种方式拼出重量0,1,...,targer,需要知道前N-1个物品有多少种方式拼出重量0,1,...,target,状态设f[i][w] = 用前i个物品有多少种方式拼出重量w,转移方程如下:
这里提出了一种空间优化的方法,基于原有的思路,可以开辟一个两行的数组进行优化,当采用从后往前的方式进行计算时,不会出现覆盖的情况,所以这里可以进一步优化。代码:
-
Backpack6,与上面题目的区别是每个重量可以重复用多次,这里需要回到一维的思路,类似于Coin Change,这里的最后一步是枚举如果最后一个物品的重量是A0,...An-1,则要求有多少种组合能拼成Target - A0,..Target - An-1,状态是f[i] = 有多少种组合能拼出重量i,转移方程如下:
这里的边界情况要注意一下,如下:
-
Backpack2,背包有最大承重,每个商品有着各自不同的价值,求背包中能放入的最大价值的物品。如果对于每个总重量,我们能知道对应的最大价值是多少,就能通过一个for循环找出答案,所以这里的状态也是从总承重的角度考虑(即重量作为数组下标,价值作为数组元素),当然也会存在某些重量拼不出来的情况,可以设为无穷小。对于背包问题,最后一步的考虑方式有两种:最后一个物品是否进入背包,以及最后进入背包的物品是哪一个!这里在实际编程时使用了一维数组进行空间优化,需要注意的是当使用一位数组时,要从后往前做,倒着计算。状态和转移方程如下:
初始条件和边界情况:
核心代码:
-
Backpack3,与上一题的区别在于每种物品都有无穷多个。从性价比的角度考虑,由于物品不能切割,所以不能使用贪心算法进行计算。状态和转移方程可以根据上一题时进行修改,即最后一个物品进入背包几次,如下:
通过画矩阵以及距离分析的方法,目标是能否减少枚举过程中的重复计算,简化之后的转移方程如下:
对于数组空间的优化,通过观察转移方程中计算的顺序,可以将一个二维问题降至一维求解,只不过这里与上面一题的区别在于计算顺序可以从前往后了,代码的修改也是只需要修改计算顺序那一部分。