代码随想录算法训练营第四十三天|322. 零钱兑换, 279. 完全平方数,139. 单词拆分
322. 零钱兑换, 279. 完全平方数,139. 单词拆分
- 322. 零钱兑换
- 279. 完全平方数
- 139. 单词拆分
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
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:dp = [-1]*(amount+1)dp[0] = 0for i in coins:for j in range(i,amount+1):if i==j:dp[j] = 1else:if dp[j] > 0 and dp[j-i] >= 0:dp[j] = min(dp[j],dp[j - i] + 1)elif dp[j] > 0 :# dp[j]>0 dp[j-i] < 0dp[j] = dp[j]elif dp[j-i] > 0:# dp[j]<=0 dp[j-i] >0dp[j] = dp[j-i]+1else:# dp[j]<=0 dp[j-i] <0dp[j] = -1return dp[amount]
参考代码,初始值赋inf,可以减少分类情况,只考虑是否是初始值状态来进行状态转移。
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:dp = [float('inf')] * (amount + 1) # 创建动态规划数组,初始值为正无穷大dp[0] = 0 # 初始化背包容量为0时的最小硬币数量为0for coin in coins: # 遍历硬币列表,相当于遍历物品for i in range(coin, amount + 1): # 遍历背包容量if dp[i - coin] != float('inf'): # 如果dp[i - coin]不是初始值,则进行状态转移dp[i] = min(dp[i - coin] + 1, dp[i]) # 更新最小硬币数量if dp[amount] == float('inf'): # 如果最终背包容量的最小硬币数量仍为正无穷大,表示无解return -1return dp[amount] # 返回背包容量为amount时的最小硬币数量
279. 完全平方数
给你一个整数 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 = [float('inf')]*(n+1)dp[0] = 0res = []for i in range(1,n+1):res.append(i**2)for i in res:for j in range(i,n+1):if dp[j-i] != float('inf'):dp[j] = min(dp[j],dp[j - i]+1)if dp[n] == float('inf'):return -1return dp[n]
和上题类似完全背包问题,需要注意n=1时候,完全平方数可以取1,所以完全平方数筛选时候要至少覆盖 r a n g e ( i , n + 1 ) range(i,n+1) range(i,n+1)中。
参考代码
class Solution:def numSquares(self, n: int) -> int:dp = [float('inf')] * (n + 1)dp[0] = 0for i in range(1, int(n ** 0.5) + 1): # 遍历物品for j in range(i * i, n + 1): # 遍历背包# 更新凑成数字 j 所需的最少完全平方数数量dp[j] = min(dp[j - i * i] + 1, dp[j])return dp[n]
139. 单词拆分
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 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
顺序有影响,所以要先遍历背包。
参考代码
class Solution:def wordBreak(self, s: str, wordDict: List[str]) -> bool:wordSet = set(wordDict)n = len(s)dp = [False] * (n + 1) # dp[i] 表示字符串的前 i 个字符是否可以被拆分成单词dp[0] = True # 初始状态,空字符串可以被拆分成单词for i in range(1, n + 1): # 遍历背包for j in range(i): # 遍历单词if dp[j] and s[j:i] in wordSet:dp[i] = True # 如果 s[0:j] 可以被拆分成单词,并且 s[j:i] 在单词集合中存在,则 s[0:i] 可以被拆分成单词breakreturn dp[n]