D46|背包问题总结篇

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只能求 有几种 这样的集合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值