583 字
3 分钟
代码随想录算法训练营第三十三天 | 动态规划 Part06

322. 零钱兑换#

LeetCode 322. Coin Change

思路:
使用带记忆化的递归,即自顶向下 + 备忘录优化。
注意:备忘录初始化为 -2,代表未计算;-1 代表无解。

class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 初始化备忘录,-2表示尚未计算
self.memo = [-2] * (amount + 1)
return self.dp(coins, amount)
def dp(self, coins, amount):
# base case:目标金额为0
if amount == 0:
return 0
# base case:目标金额为负,表示无解
if amount < 0:
return -1
# 如果已经计算过,直接返回备忘录的值
if self.memo[amount] != -2:
return self.memo[amount]
res = float('inf')
for coin in coins:
sub = self.dp(coins, amount - coin)
# 如果子问题无解,跳过
if sub == -1:
continue
res = min(res, sub + 1)
# 保存子问题结果到备忘录
self.memo[amount] = -1 if res == float('inf') else res
return self.memo[amount]

279. 完全平方数#

LeetCode 279. Perfect Squares

思路: 完全背包问题。每个数 j*j 可以使用无限次。 dp[i] 表示和为 i 的完全平方数最少数量。

class Solution:
def numSquares(self, n: int) -> int:
# 初始化dp数组,dp[i]表示组成i的最少完全平方数数量
dp = [float('inf')] * (n + 1)
dp[0] = 0 # 和为0时需要0个数
# 外层遍历背包容量
for i in range(1, n + 1):
# 内层遍历所有平方数物品
for j in range(1, int(i**0.5) + 1):
# 状态转移:dp[i] = min(dp[i], dp[i - j*j] + 1)
dp[i] = min(dp[i], dp[i - j * j] + 1)
return dp[n]

139. 单词拆分#

LeetCode 139. Word Break

思路: 自顶向下 + 备忘录。记忆化搜索从字符串某一位置开始是否可以拆分。

class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# 初始化备忘录,-1表示未计算,0表示false,1表示true
self.memo = [-1] * len(s)
return self.dp(s, 0, wordDict)
def dp(self, s, i, wordDict):
# 如果从第i个字符开始可以成功拆分,返回True
if i == len(s):
return True
# 如果已经计算过,直接返回结果
if self.memo[i] != -1:
return self.memo[i] == 1
for word in wordDict:
length = len(word)
# 如果剩余长度不足一个单词,跳过
if i + length > len(s):
continue
# 取出s中从i开始、长度为length的子串
sub = s[i:i + length]
if sub != word:
continue
# 如果子问题成功,记下结果并返回
if self.dp(s, i + length, wordDict):
self.memo[i] = 1
return True
# 所有尝试都失败,记为false
self.memo[i] = 0
return False
代码随想录算法训练营第三十三天 | 动态规划 Part06
https://fuwari.vercel.app/posts/code_musings_day_thirty-three/
作者
Jarrett
发布于
2025-07-18
许可协议
CC BY-NC-SA 4.0