Day 41 | 322. 零钱兑换 、 279.完全平方数、139.单词拆分

322. 零钱兑换

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

这句话结合本题 大家要好好理解。
视频讲解:https://www.bilibili.com/video/BV14K411R7yv
https://programmercarl.com/0322.零钱兑换.html
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

思考

本题求钱币最小个数,那么钱币有顺序和没有顺序都可以,都不影响钱币的最小个数。

所以本题并不强调集合是组合还是排列。背包和物品的遍历先后不重要。

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i] 组成总数为i的的硬币最小组合长度
        dp = [float('inf')] * (amount+1)
        dp[0] = 0
        # 先物品再背包
        for coin in coins:
            for i in range(1,amount+1):
                if i-coin >=0:
                    dp[i] = min(dp[i-coin]+1,dp[i])
        if dp[-1] == float('inf'):
            return -1
        else:
            return dp[-1]

279.完全平方数

本题 和 322. 零钱兑换 基本是一样的,大家先自己尝试做一做
视频讲解:https://www.bilibili.com/video/BV12P411T7Br
https://programmercarl.com/0279.完全平方数.html

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:

输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:

输入:n = 13
输出:2
解释:13 = 4 + 9

思考

跟零钱兑换一样,完全背包问题。
本题求钱币最小个数,那么钱币有顺序和没有顺序都可以,都不影响钱币的最小个数。
所以本题并不强调集合是组合还是排列。背包和物品的遍历先后不重要。

class Solution:
    def numSquares(self, n: int) -> int:
        # dp[i] 容量i的背包,装满的最少组合数
        dp = [float('inf')] * (n+1)
        dp[0] = 0
        #先物品再背包 求组合数
        for j in range(1,n+1):
            if j**2 > n:
                break
            for i in range(1,n+1):
                if i>=j**2:
                    dp[i] = min(dp[i],dp[i-j**2]+1)
        return dp[-1]

139.单词拆分

视频讲解:https://www.bilibili.com/video/BV1pd4y147Rh
https://programmercarl.com/0139.单词拆分.html

给定一个非空字符串 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

思考

物品可以重复选取,完全背包问题。
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
而本题其实我们求的是排列数,为什么呢。 拿 s = "applepenapple", wordDict = ["apple", "pen"] 举例。

"apple", "pen" 是物品,那么我们要求 物品的组合一定是 "apple" + "pen" + "apple" 才能组成 "applepenapple"。

"apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的,那么我们就是强调物品之间顺序。

所以说,本题一定是 先遍历 背包,再遍历物品。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        #dp[i] 表示 s[0:i]是否可以拆分 左闭右开
        dp = [False]* (len(s)+1)
        dp[0] = True
        for i in range(1,len(s)+1):
            for word in wordDict:
                if i >=len(word):
                    #print(word,len(word),i)
                    dp[i] = dp[i-len(word)] and s[i-len(word):i] == word
                    if dp[i]:
                        break  
        return dp[len(s)]

关于多重背包,你该了解这些!

https://programmercarl.com/背包问题理论基础多重背包.html

背包问题总结篇!

https://programmercarl.com/背包总结篇.html

posted @ 2024-07-07 23:25  forrestr  阅读(55)  评论(0)    收藏  举报