动态规划 | 完全背包问题 | 组成背包的`最少`元素数量| 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

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]
提前定义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]

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)]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值