• 动态规划 | 完全背包问题 | 组成背包的`最少`元素数量| leecode刷题笔记


    跟随carl代码随想录刷题
    语言:python


    • 如果求组合数就是外层for循环遍历物品,内层for遍历背包。
    • 如果求排列数就是外层for遍历背包,内层for循环遍历物品。
    • 如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有{1,3}这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!

    求组成背包的最少元素数量,无所谓组合数还是排列数,因为顺序不重要。所以遍历的时候,可以先遍历物品,再遍历背包;也可以先遍历背包,再遍历物品。

    322. 零钱兑换

    题目:给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
    计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
    你可以认为每种硬币的数量是无限的。
    👉示例 1:
    输入:coins = [1, 2, 5], amount = 11
    输出:3
    解释:11 = 5 + 5 + 1
    👉示例 2:
    输入:coins = [2], amount = 3
    输出:-1
    👉示例 3:
    输入:coins = [1], amount = 0
    输出:0

    题目分析

    动态规划五部曲

    1. dp[i]表示:凑成总金额是i最少硬币个数是dp[i]
    2. dp[i] = min(dp[i], dp[i - coins[j]]+1)
    3. 初始化:dp[0] = 0。因为递推公式中是求min,所以应该初始化一个最大的数
    4. 确定遍历顺序:本题不强调是组合还是排列,遍历顺序不重要。
    5. 举例推导dp数组

    完整代码如下

    class Solution:
        def coinChange(self, coins: List[int], amount: int) -> int:
            # 初始化
            dp = [amount + 1] * (amount + 1)
            dp[0] = 0
    
            # 遍历(顺序不重要)
            for coin in coins:
                for j in range(coin, amount + 1):
                    dp[j] = min(dp[j], dp[j - coin] + 1)
            if dp[amount] < amount + 1:
                return dp[amount]
            else:
                return -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    279. 完全平方数

    题目:给你一个整数 n ,返回 和为 n 的完全平方数的最少数量
    完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
    👉示例 1:
    输入:n = 12
    输出:3
    解释:12 = 4 + 4 + 4
    👉示例 2:
    输入:n = 13
    输出:2
    解释:13 = 4 + 9

    题目分析

    1. dp[j]表示:和为j完全平方数的最小数量是dp[j]
    2. dp[j] = min(dp[j], dp[j - i*i] + 1)
    3. 初始化:dp[0] = 0,非零下标一定要初始化为最大值,这样才不会被覆盖:``
    4. 本题求最小数,不要求是组合数还是排列数,所以两种遍历方式都可以。
    5. 举例

    完整代码如下

    在循环里使用i*i
    class Solution:
        def numSquares(self, n: int) -> int:
            # 初始化
            dp = [n + 1] * (n + 1)
            dp[0] = 0
    
            # 遍历(顺序不重要):这里选择先遍历物品,再遍历背包
            for i in range(1, n+1):  # 边界很重要
                for j in range(i*i, n + 1):
                    dp[j] = min(dp[j], dp[j - i*i] + 1)
            
            return dp[n]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    提前定义nums
    class Solution:
        def numSquares(self, n: int) -> int:
            # 初始化
            nums = [i**2 for i in range(1, n+1) if i**2 <= n]
            dp = [10**4] * (n+1)
            dp[0] = 0
            # 遍历背包
            for j in range(1, n+1):
                for num in nums:
                    if j >= num:
                        dp[j] = min(dp[j], dp[j - num] + 1)
            return dp[n]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    139. 单词拆分

    题目:给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
    注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
    👉示例 1:
    输入: s = “leetcode”, wordDict = [“leet”, “code”]
    输出: true
    解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
    👉示例 2:
    输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
    输出: true
    解释: 返回 true 因为 “applepenapple” 可以由 “apple” “pen” “apple” 拼接成。
    注意,你可以重复使用字典中的单词。
    👉示例 3:
    输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
    输出: false

    题目分析

    可以出现重复元素,所以是完全背包问题。

    1. dp[i]表示:如果字符串长度为idp[i]为True,那么表示可以拆分为一个或多个在字典中出现的单词。
    2. 如果dp[j]是True,且[j, i]区间的字符串出现在字典里,那么dp[i]一定为True(j < i)。所以,递推公式为:
      • if dp[j] and j < i and :
        • dp[i] == True
    3. 初始化:dp[i]是否为真,与前一个是否为真密切相关。所以,dp[0] == True,否则后面都是False了。
      • dp[0]表示,如果字符串为空,那么出现在字典里。
      • 题目中字符串不为空,因此,dp[0]为True完全是为了推导公式。
    4. 遍历顺序
      • 本题要求的是”是否出现过“,所以排列数组合数都可以。
      • 因为要求子串,所以最好是把背包放外层,物品放内层。
    5. 推导

    完整代码如下

    class Solution:
        def wordBreak(self, s: str, wordDict: List[str]) -> bool:
            # 初始化
            dp = [False] * (len(s) + 1)
            dp[0] = True
    
            # 遍历
            for j in range(1, len(s)+1):
                for word in wordDict:
                    if j >= len(word):
                        dp[j] = dp[j] or (dp[j - len(word)] and word == s[j - len(word):j])
                        # 如果前一个为真,并且该字符串在列表中,则返回真,否则返回Flase
            return dp[len(s)]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    win10恢复注册表MMC文件夹
    软件测试面试被问:你为何换工作、换专业学测试?
    CSDN更换背景 调整博客风格与代码块样式方法
    Eclipse配置python环境全步骤
    Linux系统编程
    ElasticSearch - 映射(mapping)
    UDS入门至精通系列:Service 23
    [ansible] playbook运用
    十年架构五年生活-06 离职的冲动
    mac idea启动没反应 无法启动
  • 原文地址:https://blog.csdn.net/qq_44250700/article/details/126422895