代码随想录算法训练营day44 | 完全背包,518. 零钱兑换 II,377. 组合总和 Ⅳ

完全背包:

  • 与01背包问题唯一不同的地方就是,

    • 理论方面:每件物品都有无限个(也就是可以放入背包多次)

    • 代码方面:在遍历顺序上。正是因为可以多次放入背包,所以要正序遍历。

  • 测试代码:代码随想录

518. 零钱兑换 II

  • 一看到钱币数量不限,就知道这是一个完全背包

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        #step1: 确定dp数组以及下标的含义: dp[j]:凑成总金额j的货币组合数为dp[j]
        #step2: 确定递推公式: dp[j] += dp[j - coins[i]] (参考#494 目标和一样)
        #step3: dp数组初始化
        dp = [0]*(amount+1)
        dp[0] = 1
        #step4: 确定遍历顺序: 
        '''
        还是遵循先物品后背包(虽说对于纯完全背包问题先遍历谁都可以,但是首先 先物品后背包 是为了与前面的遍历方式统一, 
        其次对于这道题而言只能是先物品后背包,因为求的是组合数不是排列数,对于顺序是有要求的。具体参考代码随想录)
        '''
        for i in range(len(coins)):
            for j in range(coins[i], amount+1):
                dp[j] = dp[j] + dp[j-coins[i]]
        #step5: 打印检查
        return dp[amount]

注意:

  • 如果求组合数就是外层for遍历物品,内层for遍历背包。(本题)

  • 如果求排列数就是外层for遍历背包,内层for遍历物品。

377. 组合总和 Ⅳ

  • 从例题中可以看出求的是排列

class Solution:
    def combinationSum4(self, nums: List[int], target: int) -> int:
        #step1: 确定dp数组以及下标的含义: dp[i]: 凑成目标正整数为i的排列个数为dp[i]
        #step2: 确定递推公式: dp[j] += dp[j - nums[i]] (参考#494 目标和一样)
        '''但当进行到step4时会发现需要先遍历背包后遍历物品,因此此时的i和j需要调换位置,即dp[i] += dp[i - nums[j]]'''
        #step3: dp数组如何初始化
        dp = [0]*(target+1)
        dp[0] = 1
        #step4: 确定遍历顺序
        '''
        在#518. 零钱兑换 II中讨论过为什么需要先物品后背包,那么这道题是求排列则需要先背包后物品
        '''
        for i in range(1, target+1):
            for j in range(len(nums)):
                if i >= nums[j]:
                    dp[i] += dp[i - nums[j]]
        #step5: 打印检查
        return dp[target]

注意:

  • 本题与动态规划:518.零钱兑换II (opens new window)就是一个鲜明的对比,一个是求排列,一个是求组合,遍历顺序完全不同。
  • 求装满背包有几种方法,递归公式都是一样的,没有什么差别,但关键在于遍历顺序!

你可能感兴趣的:(算法,动态规划,leetcode,python,数据结构)