代码随想录算法训练营Day46 | 139.单词拆分 | 多重背包 | 背包问题总结

news2024/9/21 12:33:52

文章目录

  • 139.单词拆分
  • 多重背包
  • 背包问题总结
    • 初始化
    • 遍历顺序

139.单词拆分

题目链接 | 理论基础

乍一看是回溯问题,和分割回文子串很像,不过本题使用回溯解决会超时(有些极端 case 过不了),而且这样只需要求解 True/False 的问题一般不会考虑回溯,毕竟回溯是暴力的指数搜索。

wordDict 看作可以无限取用的物品,s 看作是背包,问能否用物品填满背包 – 完全背包,启动!
注意到本题的结果是必须依赖于排列的,只靠组合不能确定结果,因为填满背包的方式是有顺序要求的,同样是 wordDict = ["apple", "pen"]["apple", "pen", "apple"] 就可以得到 "applepenapple",而 ["apple", "apple", "pen"] 不可以。用爬楼梯的思路来解决的话会流畅一些。

  1. dp 数组的下标含义:dp[j] 代表是否能够填满背包 s[:j]

  2. dp 递推公式:dp[j] = dp[j] or dp[j - len(wordDict[i])]

    • 不需要当前的单词 wordDict[i],就已经能组合成 s[:j]
    • 需要当前的单词 wordDict[i],并且之前已经能组成 s[:j-len(wordDict[i])]
      • “需要当前单词”代表着 s[j - len(wordDict[i]): j] == wordDict[i] 成立
    • 以上情况只要有一种成立,就能够得到 dp[j]=True
  3. dp 数组的初始化:根据递推公式可以得到 dp[0]=True,否则后面的递推无法进行

    • 从逻辑上来说,dp[0] 的取值没有明确定义,但是 dp[0]=True 能够使得 wordDict = ["pen"], s = "pen" 的情况推导出正确的 dp 数组
  4. dp 的遍历顺序:如上所述,这道题的结果依赖于排列而非组合,很明显必须是先背包再物品(爬楼梯 yyds)

  5. 举例推导:wordDict = ["leet", "code"], s = "leetcode"

    012345678
    dpTFFFTFFFT
class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        # dp[j] represents whether can make s[:j] 
        dp = [False] * (len(s) + 1)
        dp[0] = True

        # dp formula, bag->item
        for j in range(1, len(s) + 1):
            for i in range(len(wordDict)):
                if j >= len(wordDict[i]) and s[j - len(wordDict[i]): j] == wordDict[i]:
                    dp[j] = dp[j] or dp[j - len(wordDict[i])]
        
        return dp[-1]

多重背包

理论基础

给定一个限重为 V V V 的背包,有 N N N 种物品,其中第 i i i 件物品有 M i M_i Mi 个可用,每一件消耗空间 C i C_i Ci,价值是 W i W_i Wi。求怎样装入物品,使得物品总重量不会超过背包限重,同时获得最大价值。

最简单的等价转换就是将多重背包转化为 01 背包问题。将每种物品每一件都摊开来,就是 01 背包,毕竟 01 背包也没有规定物品的重量、价值不能相同。
如下,第一张表是完全背包,第二张表是 01 背包:

物品价值物品重量数量
物品 01151
物品 13203
物品 24302
物品价值物品重量
物品 0115
物品 1320
物品 1320
物品 1320
物品 2430
物品 2430

所以,只要将多重背包正确地转换成 01 背包的输入,就可以用 01 背包的方法来解决。转换代码如下:

weight = [1, 3, 4]
value = [15, 20, 30]
nums = [2, 3, 2]
bagWeight = 10

# 将数量大于1的物品展开
for i in range(len(nums)):
    while nums[i] > 1:
        weight.append(weight[i])
        value.append(value[i])
        nums[i] -= 1

另一种思路就是在物品+背包的遍历内部再加上一层使用数量的遍历。之前的 01 背包,无论是二维数组还是滚动数组,无论遍历顺序,都需要考虑“当前背包容量为 j,是否要取物品 i”;现在的滚动背包内,需要考虑“当前背包容量为 j,要取多少件物品 i”。
本质上还是很像爬楼梯。

def test_multi_pack(weight, value, nums, bagWeight):
    dp = [0] * (bagWeight + 1)

    for i in range(len(weight)):  # 遍历物品
        for j in range(bagWeight, 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])

        # 使用 join 函数打印 dp 数组
        print(' '.join(str(dp[j]) for j in range(bagWeight + 1)))

    print(dp[bagWeight])


if __name__ == "__main__":
    weight = [1, 3, 4]
    value = [15, 20, 30]
    nums = [2, 3, 2]
    bagWeight = 10
    test_multi_pack(weight, value, nums, bagWeight)

背包问题总结

理论基础

在这里插入图片描述
通过之前这一堆背包问题的练习,解决背包问题已经比较有套路了,对遍历顺序、初始化的理解也算不错。然而,将复杂的问题背景抽象成背包问题,仍然是需要经过思考的,其中最后一块石头的重量 II、目标和 绝对是这种复杂抽象的难题。
另外,很多背包问题看上去也都能用回溯算法解决,但毫无疑问都一定会超时。区别在于,背包问题依然能够依靠子问题的解来节省复杂度,而回溯算法不可避免地需要进行穷举,只不过是优雅的穷举,两者还是有本质上的区别。

代码随想录上总结了不同的递推公式。但这些背包问题刷下来,个人感觉找对了 dp 数组的含义后,dp 递推公式就能很自然地得到,无需特别考虑。相比之下,初始化和遍历顺序才是更大的坑。

初始化

背包问题的初始化堪称是五花八门,每一道题都需要谨慎思考。找到正确的初始化,最好的方法是对于自己的想法,找个简单的例子推一下,看看能否得到想要的答案。

  1. 有些简单的 dp 题,dp 数组的部分初始化根本不重要,例如爬楼梯中的 dp[0]。这种情况下,最重要的是确保自己的初始化能够正确地得到后续的结果即可。
  2. 二维数组解背包问题,最重要的是对于 i=0 的初始化,因为后续操作 dp[i][j] 的时候要用到 dp[i-1][...] 的值。二维数组的初始化一般比较直观,能够根据含义直接得到 i=0 和 j=0 时的值,通常来说也能帮助顺利得到正确的递推结果。
    • 目标和 堪称是二维数组初始化的难度巅峰。
  3. 滚动数组解背包问题,相对来说初始化更容易一些,因为这个一维数组中一般只需要初始化 dp[0],要思考的东西大大减少了。相对应的,滚动数组的初始化更为抽象,因为这里的初始化是没有发生更新之前,需要结合递推公式来得到合理的初始化值。
  4. 对于不特殊的 dp 数组位置,初始化同样要小心。无脑初始化为 0 或 -1 绝对不是明智的选择。
    • 零钱兑换 就是初始化大坑的巅峰。

遍历顺序

  • 01 背包
    • 二维数组:先物品后背包、先背包后物品都是可以的,内部循环从小到大、从大到小进行都可以
    • 滚动数组:对于背包的遍历必须是从大到小反向遍历(否则可能重复选取同一物品),从而导致必须是先物品后背包(否则每个背包容量都只能选取一件物品)
  • 完全背包
    • 二维数组仍然不在乎遍历顺序
      • 限制于二维数组总是根据之前的状态进行递推,二维数组似乎并不能解决
    • 滚动数组:纯粹的完全背包只在乎最终容量能够得到的价值,不在乎得到的方式(顺序),所以先物品后背包、先背包后物品都是可以的,背包的遍历必须是从小到大(才能捕捉到重复使用
      • 如果完全背包问题要求组合结果,那就必须是先物品后背包(相当于固定了物品的出现顺序)
      • 如果完全背包问题要求排列结果,那就必须是先背包后物品(相当于允许了任意的物品顺序)
        • dp 解决排列问题,最好的抽象方法是爬楼梯!
      • 如果完全背包问题不在意结果的顺序,那么物品、背包的先后顺序就无所谓了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/948201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全球选手逐鹿清华!首届AI药物研发算法大赛完美收官

8月26日,首届全球AI药物研发算法大赛决赛答辩暨颁奖典礼,在清华大学生物医学馆举行。来自微软研究院、中国科学院上海药物研究所、上海交通大学等单位的十五支团队,从全球878支团队中脱颖而出,进入了决赛答辩环节。 产教融合&…

演讲与口才艺术教案

演讲与口才艺术教案 第一部分:介绍演讲与口才艺术的重要性 演讲与口才艺术是一种重要的沟通技巧,对于个人和职业发展都具有重要的影响。一个优秀的演讲者和具备良好口才的人,能够有效地表达自己的观点、激发他人的兴趣,并影响他…

React 18 在组件间共享状态

参考文章 在组件间共享状态 有时候,希望两个组件的状态始终同步更改。要实现这一点,可以将相关 state 从这两个组件上移除,并把 state 放到它们的公共父级,再通过 props 将 state 传递给这两个组件。这被称为“状态提升”&#…

记一次坑爹的ARouter::There is no route match the path

接入阿里的ARouter框架;实现最简单的页面跳转;一直失败,报找不到匹配的路径;百度各种基本都说配置有问题;我一一对应,配置怎么看看不出问题来; 我的架构是 接入的代码怎么看都没问题&#xff1…

高并发幂等计数器的设计与实现

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

react css 污染解决方法

上代码 .m-nav-bar {background: #171a21;.content {height: 104px;margin: 0px auto;} }import React from "react"; import styles from ./css.module.scssexport default class NavBar extends React.Component<any, any> {constructor (props: any) {supe…

Linux常用命令——dd命令

在线Linux命令查询工具 dd 复制文件并对原文件的内容进行转换和格式化处理 补充说明 dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的&#xff0c;对于一些比较底层的问题&#xff0c;使用dd命令往往可以得到出人意料的效果。用的比较多的还是…

[ZenTao]源码阅读:加载自定义任务类型

www/index.php config/config.php framework/base/router.class.php tmp/model/common.php module/common/model.php framework/router.class.php

《动手学深度学习》-55循环神经网络

沐神版《动手学深度学习》学习笔记&#xff0c;记录学习过程&#xff0c;详细的内容请大家购买书籍查阅。 b站视频链接 开源教程链接 循环神经网络 潜变量自回归模型&#xff1a; 循环神经网络结构&#xff1a; 简单来说循环神经网络RNN就是在MLP中加了一项&#xff0c;使它可…

气传导蓝牙耳机哪个好?好用爆款气传导蓝牙耳机推荐

​对于气传导耳机&#xff0c;还有很多朋友觉得还是比较陌生&#xff0c;气传导工作原理是通过空气传播&#xff0c;由耳廓收集声音&#xff0c;然后以声波的形式引起鼓膜振动。气传导耳机除了拥有骨传导耳机优点之外&#xff0c;长时间佩戴没有震麻感&#xff0c;音质比骨传导…

alibabacloud的简单使用,nacos配置中心+服务中心。作者直接给自己写的源码

文章目录 依赖关键主要的程序启动文件配置文件bootstrap.yml依赖文件nacos配置中心上的文件截图 启动成功截图参考文档 依赖关键 SpringBoot版本和com.alibaba.cloud版本需要对应&#xff0c;不然会程序会启动失败作者使用的版本 SpringBoot: 2.1.6.RELEASE alibabacloud: 2.…

Android AGP8.1.0组件化初探

Android AGP8.1.0组件化初探 前言&#xff1a; 前面两篇完成了从AGP4.2到 AGP8.1.0的升级&#xff0c;本文是由于有哥们留言说在AGP8.0中使用ARouter组件化有问题&#xff0c;于是趁休息时间尝试了一下&#xff0c;写了几个demo&#xff0c;发现都没有问题&#xff0c;跳转和传…

循环的技巧和深入条件控制

这里对深入条件控制的知识点做一下测试&#xff1a;用作普通值而不是布尔值时&#xff0c;短路运算符的返回值通常是最后一个求了值的参数。 a2c5 for b in [0,1]:print((a and b and c))运行结果 E:\Python\Python38\python.exe D:/pythonprojects/python-auto-test/test/ti…

【MySQL】mysql connect

目录 一、准备工作 1、创建mysql用户 2、删除用户 3、修改用户密码 3.1、自己改自己密码 3.2、root用户修改指定用户的密码 4、数据库的权限 4.1、给用户授权 4.2、回收权限 二、连接mysql client 1、安装mysql客户端库 2、验证是否引入成功 三、 mysql接口 1、初…

Spring boot 整合 Okhttp3 并封装请求工具

一、 为什么要使用okHttp OkHttp是一个高效、灵活、易于使用的HTTP客户端库&#xff0c;优势如下&#xff1a; 性能更高&#xff1a;OkHttp在网络请求处理上采用了异步模型&#xff0c;并将连接池、压缩、网络协议等多种技术应用到其中&#xff0c;从而提高了网络请求的效率和…

c语言每日一练(12)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

MongoDB实验——在MongoDB集合中查找文档

在MongoDB集合中查找文档 一、实验目的二、实验原理三、实验步骤1.启动MongoDB数据库、启动MongoDB Shell客户端2.数据准备-->person.json3.指定返回的键4 .包含或不包含 i n 或 in 或 in或nin、$elemMatch&#xff08;匹配数组&#xff09;5.OR 查询 $or6.Null、$exists7.…

Vue3 学习 组合式API setup语法糖 响应式 指令 DIFF(一)

文章目录 前言一、Composition Api二、setup语法糖三、响应式refreactive 四、其他一些关键点v-prev-oncev-memov-cloak 五、虚拟Dom五、diff算法 前言 本文用于记录学习Vue3的过程 一、Composition Api 我觉得首先VUE3最大的改变就是对于代码书写的改变&#xff0c;从原来选择…

《自然语言处理》chapter7-预训练语言模型

这是阅读《自然语言处理-基于预训练模型的方法》的学习笔记&#xff0c;记录学习过程&#xff0c;详细的内容请大家购买书籍查阅。 同时参考沐神的两个视频&#xff1a; GPT&#xff0c;GPT-2&#xff0c;GPT-3 论文精读【论文精读】 BERT 论文逐段精读【论文精读】 概述 自然…

如何保证跨境传输的安全性?

随着互联网时代的到来&#xff0c;全球文件传输频率不断增加&#xff0c;市场经济的发展也对信息共享提出更高要求。传统电话交流已无法满足跨国企业的需求&#xff0c;企业内部诸如Web、电子邮件、企业资源计划&#xff08;ERP&#xff09;、网络电话&#xff08;VOIP&#xf…