代码随想录算法训练营DAY46|C++动态规划Part8|139.单词拆分、多重背包理论基础、背包问题总结篇

news2024/12/27 11:44:57

文章目录

  • 139.单词拆分
    • 思路
    • CPP代码
  • 多重背包理论基础
    • 处理输入
    • 把所有个数大于1的物品展开成1个
    • 开始迭代,计算dp数组
    • 代码优化
  • 背包问题总结篇

139.单词拆分

力扣题目链接

文章讲解:139.单词拆分

视频讲解:你的背包如何装满?| LeetCode:139.单词拆分

本题其实是可以使用回溯算法来写的,具体可以直接去看卡哥的文章:回溯算法和优化思路

如果使用动态规划应该怎么写呢?

题目中要求我们用wordDict中的元素,看能不能组合成字符串s,其实已经很直观了!

也就是说我们的物品是wordDict,背包容量是s,看这些物品能不能正好装满这个背包。同时,题目中说了我们的物品元素是能够使用多次的,所以本题是一道很奈斯的完全背包问题!

思路

  • dp数组含义

如果这个字符串长度为i,如果能被wordDict的单词组成的话,那么dp[i]=true,最终的结果其实就是dp[s.size]到底是true还是false(我们用dp[0]来表示空字符串的状态)

  • 递推公式

这个递推公式其实蛮复杂的该说不说。这里数形结合进行表示

如果 我们的dp[j]为true,并且我们的N子段为wordDict中的元素,那就可以完美推导出dp[i]也为true

if (wordSet.find(N) != wordSet.end() && dp[j]==true)
  	dp[i]=true 
  • 初始化

dp[0]初始化成true,如果dp[0]初始化成false的话对导致后面的推导全部是false。然后非零下标为了不影响赋值所以都初始化成false

  • 遍历顺序

对于完全背包问题,两层for循环的讨论非常有必要,这里再次做总结

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

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

求组合数的题有:518.零钱兑换II

求排列数的题有:377. 组合总和 Ⅳ 、70. 爬楼梯进阶版(完全背包)

求最小数的题有:322. 零钱兑换 、279.完全平方数


在本题中,我们求的很明显是排列数,也就是说关注顺序,比如说s = “applepenapple”, wordDict = [“apple”, “pen”]

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

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

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

  • 打印

以输入: s = “leetcode”, wordDict = [“leet”, “code”]为例,dp状态如图:

CPP代码

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(), wordDict.end());
        vector<bool> dp(s.size() + 1, false);
        dp[0] = true;
        for (int i = 1; i <= s.size(); i++) {   // 遍历背包
            for (int j = 0; j < i; j++) {       // 遍历物品
                string word = s.substr(j, i - j); //substr(起始位置,截取的个数)
                if (wordSet.find(word) != wordSet.end() && dp[j]) {
                    dp[i] = true;
                }
            }
        }
        return dp[s.size()];
    }
};
  • 时间复杂度:O(n^3),因为substr返回子串的副本是O(n)的复杂度(这里的n是substring的长度)
  • 空间复杂度:O(n)

多重背包理论基础

卡码网第56题

文章讲解:多重背包理论基础

什么叫多重背包问题呢?其实就是在01背包的基础上多了一个物品数量的维度。

重量价值数量
物品01152
物品13203
物品24302

可以转换成01背包问题吗?必须的

重量价值数量
物品01151
物品01151
物品13201
物品13201
物品13201
物品24301
物品24301

处理输入

题目中谈到,“宇航舱最大容量为C”,即背包的最大容量为C;
“有N种不同的矿石”,表示物品的种类有N个;
每种矿石有一个重量w[i],一个价值v[i],以及最多k[i]个可用,这段描述可以确定是典型的多重背包问题。输入形式如下:

输入共包括四行,第一行包含两个整数 C 和 N,分别表示宇航舱的容量和矿石的种类数量。
接下来的三行,每行包含 N 个正整数。具体如下:
第二行包含 N 个整数,表示 N 种矿石的重量。
第三行包含 N 个整数,表示 N 种矿石的价格。
第四行包含 N 个整数,表示 N 种矿石的可用数量上限。

int bagWeight, n;
cin >> bagWeight >> n;
vector<int> weight(n, 0);
vector<int> value(n, 0);
vector<int> nums(n, 0);
for (int i = 0; i < n; i++) cin >> weight[i];
for (int i = 0; i < n; i++) cin >> weight[i];
for (int i = 0; i < n; i++) cin >> weight[i];

把所有个数大于1的物品展开成1个

展开的过程不在乎顺序,直接往对应数组后面插即可

for (int i = 0; i < n; i++) {
	while (nums[i] > 1) { //物品数量不是一的,都展开
		weight.push_back(weight[i]);
		value.push_back(value[i]);
		nums[i]--
	}
}

开始迭代,计算dp数组

完全按照01背包的搞法来整

vector<int> dp(bagWeight + 1, 0);
for (int i = 0; i < weight.size(); i++) {
	for (int j = bagWeight; j >= weight[i]; j--) {
		dp[j] = max(dp[j], dp[j - weight[i]] + value[i];
	}
	cout << dp[bagWeight] << endl;
}

代码优化

如果提交刚刚那段代码,我们会发现代码超时了!
我们再来审视一下之前的代码,其实出问题的就在于vector底层的动态扩容,虽然根据扩容操作的平摊事件时间复杂度,vector底层的动态扩容应该是O(1),但是数据量较大的时候还是会遇到插入延迟,所以我们应该对其进行优化。

for (int i = 0; i < n; i++) {
    while (nums[i] > 1) { // 物品数量不是一的,都展开
        weight.push_back(weight[i]);
        value.push_back(value[i]);
        nums[i]--;
    }
}
  • 我们可以先把所有物品数量都计算好,然后一起申请vector的空间。
    //方法:reserve
    // 计算总共需要的空间
    int totalItems = 0;
    for (int i = 0; i < n; i++) {
        totalItems += nums[i];
    }

    vector<int> expandedWeight;
    vector<int> expandedValue;
    expandedWeight.reserve(totalItems); // 预分配足够的空间
    expandedValue.reserve(totalItems);

    // 填充物品
    for (int i = 0; i < n; i++) {
        for (int count = 0; count < nums[i]; count++) {
            expandedWeight.push_back(weight[i]);
            expandedValue.push_back(value[i]);
        }
    }

	//方法二:resize
	// 计算总共需要的空间
    int totalItems = 0;
    for (int i = 0; i < n; i++) {
        totalItems += nums[i];
    }

    // 使用 resize 预设大小,并在原数组上操作
    weight.resize(totalItems);
    value.resize(totalItems);

    int index = n; // 从原始数组大小开始填充新元素
    for (int i = 0; i < n; i++) {
        for (int count = 1; count < nums[i]; count++) { // 已有一个初始元素,所以从1开始
            weight[index] = weight[i];
            value[index] = value[i];
            index++;
        }
    }
  • 再就是把每种商品遍历的个数放在01背包里面一起遍历
//最终代码
#include<iostream>
#include<vector>
using namespace std;
int main() {
    int bagWeight,n;
    cin >> bagWeight >> n;
    vector<int> weight(n, 0);
    vector<int> value(n, 0);
    vector<int> nums(n, 0);
    for (int i = 0; i < n; i++) cin >> weight[i];
    for (int i = 0; i < n; i++) cin >> value[i];
    for (int i = 0; i < n; i++) cin >> nums[i];

    vector<int> dp(bagWeight + 1, 0);

    for(int i = 0; i < n; i++) { // 遍历物品
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            // 以上为01背包,然后加一个遍历个数
            for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数
                dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]);
            }
        }
    }

    cout << dp[bagWeight] << endl;
}

时间复杂度:O(m × n × k),m:物品种类个数,n背包容量,k单类物品数量

背包问题总结篇

背包问题中关于初始值的设定只有两种情况:

  1. 关于下标0的设定紧贴dp数组的含义
  2. 关于非零下标的设置要防止后续推导的值被覆盖

二刷后完成总结,这里先贴上卡哥的文章背包问题总结篇

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

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

相关文章

体育老师工资高吗,奖金有吗

教师的薪资水平与多种因素相关&#xff0c;包括教育经验、工作地点、学校类型以及个人的教学成果等。在讨论体育教师的工资问题时&#xff0c;不能仅仅关注数字&#xff0c;更应了解教育价值和个人发展。 初中体育教师的工资水平受多种因素影响。根据网络统计的数据&#xff0c…

STM32F4xx开发学习—GPIO

GPIO 学习使用STM32F407VET6GPIO外设 寄存器和标准外设库 1. 寄存器 存储器映射 存储器本身是不具有地址的&#xff0c;是一块具有特定功能的内存单元&#xff0c;它的地址是由芯片厂商或用户分配&#xff0c;给存储器分配地址的过程就叫做存储区映射。给内存单元分配地址之后…

页面多开、谷歌浏览器解决不能批量打开问题、批量打开被限制

目录 问题原因谷歌浏览器解决办法来看效果 问题 我们使用批量打开页面的时候 只能打开第一个页面 原因 这种问题是因为 浏览器限制了浏览器的弹出 并不是人家页面功能不能用 谷歌浏览器解决办法 在浏览器输入这个路径 chrome://settings/content/popups?search%E9%87%…

Vue MVVM这一篇就够啦!

Vue vs React 相似之处: 它们都有使用 Virtual DOM虚拟DOM-CSDN博客&#xff1b;提供了响应式&#xff08;Reactive&#xff09;和组件化&#xff08;Composable&#xff09;的视图组件。将注意力集中保持在核心库&#xff0c;而将其他功能如路由和全局状态管理交给相关的库。R…

Python安装以及环境配置

目录 一、下载安装包二级目录三级目录 一、下载安装包 方式网址Python官网python.org镜像下载地址Download pythonpycharmDownload PyCharm https://blog.csdn.net/sun80760/article/details/135256627 二级目录 三级目录

Spring+SpringMVC+Jsp实现校园二手交易系统

前言介绍 在社会快速发展的影响下&#xff0c;使校园二手交易系统的管理和运营比过去十年更加理性化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上校园二手交易系统是一项十分重要并且有价值的事情。对于传统的管理控制模型来说&#xff0c;网上校园二手交易系…

C++构造函数和析构函数的调用顺序

一般情况下&#xff0c;调用析构函数的次序正好与调用构造函数的次序相反&#xff0c;也就是最先被调用的构造函数&#xff0c;其对应的析构函数最后被调用&#xff0c;而最后被调用的构造函数&#xff0c;其对应的析构函数最先被调用。 当然对象的构造函数和析构函数调用时机和…

图片浏览器-PicView

一、前言 PicView 是一款适用于 Windows 10 或 11 的快速高效的图像查看器&#xff0c;配备了干净简洁的用户界面&#xff0c;可以在不需要时方便地隐藏。 二、支持类型 它支持广泛的图像文件类型&#xff0c;包括&#xff1a;WEBP、GIF、SVG、PNG、JXL、HEIC、PSD 三、软件特…

Docker 的网络实现

简介 标准的 Docker 支持以下 4 类网络模式&#xff1a; 1&#xff09;host 模式&#xff1a;使用 --nethost 指定 2&#xff09;container 模式&#xff1a;使用–netcontainer:NAME_or_ID 指定 3&#xff09;none模式&#xff1a;使用 --netnone 指定 4&#xff09;bridge 模…

2.5W字 一文读懂汽车智能座舱的FLASH 存储市场、技术

吃瓜群众&#xff1a;机哥&#xff0c;存储是什么玩意&#xff0c;我买手机、电脑的时候导购员都说买内存大的&#xff0c;三星的好&#xff0c;品牌大&#xff0c;问题少&#xff0c;我也只有看哪个内存大就买那个。 机哥&#xff1a;额&#xff0c;这个嘛&#xff0c;说来话长…

SpringBoot+Vue+Element-UI实现学生综合成绩测评系统

前言介绍 学生成绩是高校人才培养计划的重要组成部分&#xff0c;是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。而学生所在学院多采用半手工管理学生成绩的方式&#xff0c;所以有必要开发学生综合成绩测评系…

Day62:单调栈 LeedCode503. 下一个更大元素 II 42. 接雨水

503. 下一个更大元素 II 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#xff0c;这个数字之后的第一个比它更大的数…

分布式与一致性协议之ZAB协议(三)

ZAB协议 主节点崩溃了&#xff0c;怎么办&#xff1f; 众所周知&#xff0c;系统在运行中不可避免会出现各种各样的问题&#xff0c;比如进程崩溃了、服务器死机了&#xff0c;这些问题会导致很严重的后果&#xff0c;让系统没办法继续运行。在ZAB协议中&#xff0c;写请求是…

【软考】模拟考卷错题本2024-05-05

1 算法 关键词&#xff1a;按照单位重量价值大优先&#xff0c;那就是1、2、3即430&#xff1b;之后的根据排除法又可以得到630&#xff1b;故C。 2 UML 序列图 上图已经基本上有解析&#xff1b;重点在于在四个选项中选正确的。根据概念排除&#xff1a;异步和同步是不一样的&…

GateWay检查接口耗时

添加gateway依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId> </dependency>创建一个LogTimeGateWayFilterFactory类&#xff0c;可以不是这个名字但是后面必须是x…

opencv基础篇 ——(十六)图形绘制与填充

OpenCV 提供了丰富的图形绘制和填充功能&#xff0c;主要通过 cv::rectangle, cv::circle, cv::line, cv::polylines, cv::fillPoly 和 cv::ellipse 等函数实现。以下是一些基本的图形绘制和填充操作的说明&#xff1a; 矩形: 函数: cv::rectangle语法: cv::rectangle(img, rec…

10000 字详细讲解 Spring 中常用注解及其使用

如下图京东购物页面&#xff0c;当我们选择点击访问某一类商品时&#xff0c;就会向后端发起 HTTP 请求&#xff0c;当后端收到请求时&#xff0c;就会找到对应的代码逻辑对请求进行处理&#xff0c;那么&#xff0c;后端是如何来查找处理请求相对应的代码呢&#xff1f;答案就…

JRT1.6发布

经过51的三天努力&#xff0c;完成基于JRT的质控核心部分。框架部分已经达到了第一个可生产版本。 可生产包括以下部分&#xff1a; 1.Web开发基础和发布运维基础 2.Linux和WIndows客户端浏览器 3.Linux和WIndows客户端打印导出程序 4.Linux和WIndows初始化程序 5.Linux和WInd…

调用第三方接口——支付宝付款

沙箱环境是支付宝开放平台为开发者提供的用于接口开发及主要功能联调的模拟环境。 参考 登录 - 支付宝 在沙箱环境下&#xff0c;已经分配好了用于模拟测试的应用信息、商家信息、买家信息等 小程序文档 - 支付宝文档中心 内网穿透&#xff08;支付宝付款需要在公网进行检查…

谁能取代迈巴赫,征服互联网安全大佬周鸿祎?

‍作者 |老缅 编辑 |德新 4月18日&#xff0c;「周鸿祎卖车」登上了微博热搜。这位360创始人、董事长发微博称&#xff1a;自己做了一个艰难的决定&#xff0c;将把陪伴9年的迈巴赫600给卖掉。 随后&#xff0c;他解释道&#xff1a;「这是因为我需要体验新一代车的感觉。古人…