完全背包理论
完全背包简单来说就是物品的数量是无数个。可以对一个物品重复选取。
重点来了:由于前面对于0,1背包中,我们要采取逆序,这样才能保证不会重复选取,而对于完全背包来说,要采取正序
完全背包中有两种常考题,一种是组合,一种是排列,比如求装满背包的方式,在这些方式的定义中,若1,2 和2,1是同一种,那就是排列问题,组合问题使用常规的物品作为外层循环,背包作为内层循环
而对于排列问题,则是使用背包作为外层循环,排列作为内层循环,注意,由于背包作为外层循环了,没法使用nums[i]作为起始点,只能用1,所以此处,写递推公式的时候,还需要加上判断语句,如j >= nums[i],这里的j仍旧是背包循环中的元素,其余与先前保持一致。
对于初始化问题,一般来说,我们是求最大值,求Max,初始化时,为了不影响取值,我们往往对元素初始化为0,但是有一种问题是,求满足背包的最小方案(元素最少),就是求min,那么我们初始化时应该取一个很大的值,比如float("INF")或者题目规定的最大取值。此处往往将dp[0]设为0
求最小方案问题时,递推公式中的,+value[i],此时应该换成+1,因为我们的目标是求方案长度(方案中元素数量),这体现的就是要求我们灵活改变公式
解题思路大致可以归为:1. 判断是01背包还是完全背包,通过物品数量决定,由此判断正逆序的使用。
2.判断是求最大数量还是常规问题,最大数量使用累加公式。
3. 求min还是max,决定初始化,设计到累加问题,一般来说dp[0]初始化为1。
4.若是完全背包,则判断是排列问题还是组合问题,决定内外层循环是啥
Leetcode - 518
硬币无限个,完全背包,正序。求所有可能,累加问题。
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount+1)
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i],amount+1):
dp[j] += dp[j-coins[i]]
return dp[-1]
Leetcode - 377
数据无限量使用,完全背包,正序。 求所有可能,累加问题。不同顺序就是不同的结果,很明显是排列问题,所以for循环我们要注意
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [0] * (target +1)
dp[0] = 1
for j in range(1,target +1):
for i in range(len(nums)):
if j >= nums[i]:
dp[j] += dp[j-nums[i]]
return dp[-1]