一、322. 零钱兑换
题目链接:322. 零钱兑换 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——322. 零钱兑换
视频讲解:动态规划之完全背包,装满背包最少的物品件数是多少?| LeetCode:322.零钱兑换_哔哩哔哩_bilibili
动态规划五部曲:
1. 确定 dp 数组及下标含义:装满容量为 j 的背包最少 dp[ j ]件物品,最后结果 dp[ amount ]
2. 确定递推公式:dp[ j ] = min( dp[ j - coins[ i ]] + 1, dp[ j ]),dp[ j - coins[ i ]] 表示装满 j - coins[ i ] 最的数量,再加上 coins[ i ] 的价值 1
3. 确定dp数组如何初始化:dp[ 0 ] = 0,因为递推为取最小值,所以非零下边初始化为最大值 inf
4. 确定遍历顺序:先遍历物品或先遍历背包均可。
# 先遍历物品
for coin in coins:
# 再遍历背包
for j in range(coins[ i ], amount + 1):
递推公式
5. 举例推导dp数组。
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 创建dp数组
dp = [float("inf")] * (amount + 1)
# 初始化
dp[0] = 0
# 先遍历物品,即硬币
for coin in coins:
# 再遍历背包
for j in range(coin, amount + 1):
dp[j] = min(dp[j - coin] + 1, dp[j])
# 如果仍为初始值,即表示无法组合,返回 -1
if dp[amount] == float("inf"):
return -1
return dp[amount]
二、279.完全平方数
题目链接:279. 完全平方数 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——279.完全平方数
视频讲解:动态规划之完全背包,换汤不换药!| LeetCode:279.完全平方数_哔哩哔哩_bilibili
思路:题意也就是给你一个容量为 n 的背包,有一堆价值为 1, 4, 9,16…的物品,问装满这个背包最少需要多少个物品?和上一题零钱兑换一样。
动态规划五部曲:
1. 确定 dp 数组及下标含义:装满容量为 j 的背包最少 dp[ j ]件物品,最后结果 dp[ n ]
2. 确定递推公式:dp[ j ] = min(dp[ j - i ** 2 ] + 1, dp[ j ])
3. 确定dp数组如何初始化:dp[ 0 ] = 0,因为递推为取最小值,所以非零下边初始化为最大值 inf
4. 确定遍历顺序:先遍历物品或先遍历背包均可。
# 先遍历物品
for i in range(1, int(n ** 0.5) + 1):
# 再遍历背包
for j in range(i * i, n + 1):
递推公式
5. 举例推导dp数组。
class Solution:
def numSquares(self, n: int) -> int:
# 创建dp数组
dp = [float("inf")] * (n + 1)
# 初始化
dp[0] = 0
# 先遍历物品
for i in range(1, int(n ** 0.5) + 1):
# 后遍历背包
for j in range(i * i, n + 1):
dp[j] = min(dp[j - i ** 2] + 1, dp[j])
return dp[n]
三、139.单词拆分
题目链接:139. 单词拆分 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——139.单词拆分
视频讲解:动态规划之完全背包,你的背包如何装满?| LeetCode:139.单词拆分_哔哩哔哩_bilibili
思路:字符串看作背包,单词为物品,问物品能否装满背包。
动态规划五部曲:
1. 确定 dp 数组及下标含义:长度为 i 的字符串,能被单词组成,dp[ i ] = True,输出 dp[ len(s) ]
2. 确定递推公式:if s[ j : i ] and dp[ j ] == True: dp[ i ] = True
3. 确定dp数组如何初始化:dp[ 0 ] = True,非 0 下标都初始化为 False
4. 确定遍历顺序:装满背包对物品顺序有要求,因此需要先遍历背包,再遍历物品
# 先遍历背包
for i in range(1, len(s) + 1):
# 再遍历物品
for j in range(i):
递推公式
5. 举例推导dp数组。
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# 创建全为 False 的 dp 数组
dp = [False] * (len(s) + 1)
# 初始化
dp[0] = True
# 先遍历背包
for i in range(1, len(s) + 1):
# 再遍历物品,即单词
for j in range(i):
if s[j:i] in wordDict and dp[j] == True:
dp[i] = True
break
return dp[len(s)]
四、多重背包理论基础
题目链接:56. 携带矿石资源(第八期模拟笔试) (kamacoder.com)
文章讲解:代码随想录 (programmercarl.com)——多重背包理论基础
将物品数量展开就变成了0-1背包问题。
五、背包问题总结
递推公式
问能否能装满背包(或者最多装多少):dp[ j ] = max(dp[ j ], dp[ j - nums[ i ]] + nums[ i ]),对应题目:
问装满背包有几种方法:dp[ j ] += dp[ j - nums[ i ]],对应题目:
- 494. 目标和 - 力扣(LeetCode)
- 518. 零钱兑换 II - 力扣(LeetCode)
- 377. 组合总和 Ⅳ - 力扣(LeetCode)
- 57. 爬楼梯(第八期模拟笔试) (kamacoder.com)
问背包装满最大价值:dp[ j ] = max(dp[ j ], dp[ j - weight[ i ]] + value[ i ]),对应题目:
问装满背包所有物品的最小个数:dp[ j ] = min(dp[ j - coins[ i ]] + 1, dp[ j ]),对应题目:
遍历顺序
0-1背包
二维0-1 dp数组背包问题中,先遍历物品还是先遍历背包均可,且第二层循环从小到大正序遍历;
一维0-1 dp数组背包问题中,必须先遍历物品,再遍历背包,且背包从大到小倒序遍历。
完全背包
纯完全背包问题,先遍历物品还是先遍历背包均可,且第二层循环从小到大正序遍历;
如果为排列问题,即强调顺序,必须先遍历背包,再遍历物品;
如果为组合问题,即不强调顺序,必须先遍历物品,再遍历背包。