代码随想录算法训练营第四十九天| 139.单词拆分、多重背包

139.单词拆分

文档讲解:代码随想录

题目链接:. - 力扣(LeetCode)

第一想法:

 非空字符串 s :背包

非空单词的列表 wordDict:物品

每个物品可以使用多次,是一个完全背包问题

看到这道题目的时候,大家应该回想起我们之前讲解回溯法专题的时候,讲过的一道题目回溯算法:分割回文串 (opens new window),就是枚举字符串的所有分割情况。

回溯算法:分割回文串 (opens new window):是枚举分割后的所有子串,判断是否回文。

本道是枚举分割所有字符串,判断是否在字典里出现过

确定dp数组以及下标的含义 

dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词

为什么用长度来表示,长度相同,也不一定结果一样

确定递推公式(妙)

如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。

所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true。

dp数组如何初始化

dp[0] = True

从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定要为true,否则递推下去后面都都是false了。

那么dp[0]有没有意义呢?

dp[0]表示如果字符串为空的话,说明出现在字典里。

但题目中说了“给定一个非空字符串 s” 所以测试数据中不会出现i为0的情况,那么dp[0]初始为true完全就是为了推导公式。

下标非0的dp[i]初始化为false,只要没有被覆盖说明都是不可拆分为一个或多个在字典中出现的单词。

确定遍历顺序

题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。

还要讨论两层for循环的前后顺序。

如果求组合数就是外层for循环遍历物品,内层for遍历背包

如果求排列数就是外层for遍历背包,内层for循环遍历物品

我在这里做一个总结:

求组合数:动态规划:518.零钱兑换II (opens new window)

求排列数:动态规划:377. 组合总和 Ⅳ (opens new window)动态规划:70. 爬楼梯进阶版(完全背包) (opens new window)

求最小数:动态规划:322. 零钱兑换 (opens new window)动态规划:279.完全平方数(opens new window)

而本题其实我们求的是排列数,为什么呢。 拿 s = "applepenapple", wordDict = ["apple", "pen"] 举例。

"apple", "pen" 是物品,那么我们要求 物品的组合一定是 "apple" + "pen" + "apple" 才能组成 "applepenapple"。

"apple" + "apple" + "pen" 或者 "pen" + "apple" + "apple" 是不可以的,那么我们就是强调物品之间顺序。

所以说,本题一定是 先遍历 背包,再遍历物品。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        dp = [False] * (len(s)+1)
        print(len(s))
        dp[0] = True
        #有顺序,先遍历背包,后遍历物品
        for j in range(1,len(s)+1):
            #物品不是i,而是s[i,j]之间的字符串
            for i in range(0,j):
                if dp[i] == True and s[i:j] in wordDict:

                    dp[j] = True
                    break
                    
                    
        return dp[len(s)]

多重背包

文档讲解:代码随想录

题目链接:56. 携带矿石资源(第八期模拟笔试)

有N种物品和一个容量为V 的背包。第i种物品最多有Mi件可用,每件耗费的空间是Ci ,价值是Wi 。求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量,且价值总和最大。

多重背包和01背包是非常像的, 为什么和01背包像呢?

每件物品最多有Mi件可用,把Mi件摊开,其实就是一个01背包问题了。

# 接收输入:容量C和矿石种类数量N
C, N = map(int, input().split())

# 接收输入:矿石的重量、价格和可用数量上限
weights = list(map(int, input().split()))
prices = list(map(int, input().split()))
limits = list(map(int, input().split()))

def main(C, N,weights,prices,limits):
    dp = [0] * (C+1)
    
    for i in range(N):#遍历物品
        for j in range(C,weights[i]-1,-1): #遍历背包
            for k in range(1, limits[i] + 1):  # 遍历数量限制
                if k * weights[i] <= j:
                    dp[j] = max(dp[j],dp[j-k*weights[i]]+k*prices[i])
                else:
                    break

    return dp[C]
print(main(C, N,weights,prices,limits))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值