139.单词拆分
1.题目
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
2.实现
困难点:开始用回溯解决,但超时了,记忆数组的方法还不太理解
dp的思路:对于dp[i]的定义没想明白
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
# # 回溯方法 超时了
# res = False
# def backtra(ss):
# nonlocal res
# if res or len(ss) > len(s):
# return
# if ss == s:
# res = True
# return
# for u in wordDict:
# backtra(ss+u)
# backtra("")
# return res
# dp[i]表示是否能凑成字母i 讲究排列则外层背包,内层物品
n = len(s)
dp = [False] * (n + 1) # 必须从字母为空时讨论,这样后续递推更新才有意义
dp[0] = True
for j in range(n + 1):
for u in wordDict:
if j >= len(u) or not dp[j]:
if dp[j - len(u)] and u == s[j - len(u):j]: # dp和s对应位置不太一样,但找好准点,字母j即一共j个字母,于是只需对s往前找j - len(u)
dp[j] = True
return dp[n]
建议三刷,对于dp的定义不是很熟悉,且没想好dp什么时候变为True
多重背包理论
宗旨:化多重背包为01背包
def test_multi_pack1():
'''版本一:改变物品数量为01背包格式'''
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bag_weight = 10
for i in range(len(nums)):
# 将物品展开数量为1
while nums[i] > 1:
weight.append(weight[i])
value.append(value[i])
nums[i] -= 1
dp = [0]*(bag_weight + 1)
# 遍历物品
for i in range(len(weight)):
# 遍历背包
for j in range(bag_weight, weight[i] - 1, -1):
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
print(" ".join(map(str, dp)))
def test_multi_pack2():
'''版本:改变遍历个数'''
weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bag_weight = 10
dp = [0]*(bag_weight + 1)
for i in range(len(weight)):
for j in range(bag_weight, weight[i] - 1, -1):
# 以上是01背包,加上遍历个数
for k in range(1, nums[i] + 1):
if j - k*weight[i] >= 0:
dp[j] = max(dp[j], dp[j - k*weight[i]] + k*value[i])
print(" ".join(map(str, dp)))
if __name__ == '__main__':
test_multi_pack1()
test_multi_pack2()
背包问题总结篇
1.解决思路:
step1:根据问题定义dp,从而确定是否可以转化为背包问题,明确容量(限制)、物品(更新条件)、价值(目标),来确定具体背包类型(01 或 完全 或 多重)
(update)如何判断是否为背包问题呢?
抽象出来的常见问题:
(1)能否装满背包(在容量限制下最多装多少,dp[j] == j?)
(2)装满背包的组合个数(dp[j]表示容量为j的物品组合个数)
(3)背包装满最大价值
(4)装满背包所有物品的最小个数
step2:确定递推公式
(1)能否装满背包:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
(2)装满背包的组合个数:dp[j] += dp[j - nums[i]]
(3)背包装满最大价值:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
(4)装满背包所有物品的最小个数:dp[j] = min(dp[j - coins[i]] + 1, dp[j])
step3:初始化
注意dp[0]既要具备显示意义,又要符合递推的推导
step4:确定遍历顺序
对于01背包,为避免重复选择物品,所以外层物品,内层背包且倒序
对于完全背包,视问题类型而定,如最小数遍历顺序都可,如组合数则外层物品,内层背包正序,如排列数则相反。(记住哦!)
2.回溯与dp的辨别:
回溯是可以把 具体的集合 都列出来的, 会 dp只能求 有几种 这样的集合