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

文章目录

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

139.单词拆分

力扣题目链接

文章讲解:139.单词拆分

视频讲解:你的背包如何装满?| LeetCode:139.单词拆分
状态:本题主要还是两个地方要注意:一个是s.substr的使用,另外一个就是遍历顺序,最后是对内循环循环终止条件的考量.

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

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

题目中要求我们用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(因为我们仍然要借助前面的dp[j]来判断后面的dp[i])。然后非零下标为了不影响赋值所以都初始化成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.mfbz.cn/a/600637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

暗区突围联机不了联机失败无法联机的极速解决方法

暗区突围联机不了/联机失败/无法联机的极速解决方法 《暗区突围》是由腾讯魔方工作室群开发的第一人称射击类手游&#xff0c;于2021年8月17日进行先锋测试&#xff0c;并在2022年7月13日正式公测。《暗区突围》提供了双模式玩法&#xff0c;包括战术行动和伪装潜入&#…

QGraphicsView实现简易地图10『自适应窗口大小』

前文链接&#xff1a;QGraphicsView实现简易地图9『层级缩放显示底图』 自适应窗口大小 当地图窗口放大或缩小的时候&#xff0c;需要地图能够动态覆盖整个视口。 1、动态演示效果 2、核心代码 注&#xff1a;WHMapView继承自MapViewvoid WHMapView::resize() {if (m_curLev…

Day 26 数据库日志管理

数据库日志管理 一&#xff1a;日志管理 1.日志分类 ​ 错误日志 &#xff1a;启动&#xff0c;停止&#xff0c;关闭失败报错。rpm安装日志位置 /var/log/mysqld.log ​ 通用查询日志&#xff1a;所有的查询都记下来 ​ 二进制日志&#xff1a;实现备份&#xff0c;增量备份…

开发组合php+mysql 人才招聘小程序源码搭建 招聘平台系统源码+详细图文搭建部署教程

随着互联网的快速发展&#xff0c;传统的招聘方式已经不能满足企业和求职者的需求。为了提高招聘效率&#xff0c;降低招聘成本&#xff0c;越来越多的人开始关注人才招聘小程序、在线招聘平台。分享一个人才招聘小程序源码及搭建&#xff0c;让招聘更加高效便捷。系统是运营级…

什么是光伏发电?什么是分布式光伏系统?

一、光伏发电 光伏发电&#xff0c;作为一种可再生能源利用技术&#xff0c;其核心原理基于半导体的光生伏特效应。简而言之&#xff0c;光伏发电就是将太阳能直接转换为电能的过程。它由三个主要部分组成&#xff1a;太阳电池板&#xff08;组件&#xff09;、控制器和逆变器…

STM32F10x移植FreeRTOS

一、获取FreeRTOS源码 &#xff08;1&#xff09;登录FreeRTOS官网&#xff1a;www.freertos.org&#xff0c;下载第一个压缩包 &#xff08;2&#xff09;通过GitHub网站&#xff1a;github.com/FreeRTOS/FreeRTOS下载&#xff0c;由于该网站服务器在国外&#xff0c;所以访问…

回归预测 | Matlab实现基于CNN-SE-Attention-ITCN多特征输入回归组合预测算法

回归预测 | Matlab实现基于CNN-SE-Attention-ITCN多特征输入回归组合预测算法 目录 回归预测 | Matlab实现基于CNN-SE-Attention-ITCN多特征输入回归组合预测算法预测效果基本介绍程序设计参考资料 预测效果 基本介绍 【模型简介】CNN-SE_Attention结合了卷积神经网络&#xff…

好消息|5月6日起换发补发出入境证件可“全程网办”

国家移民管理局从2024年5月6日起&#xff0c;实施若干便民利企出入境管理的六项政策措施&#xff0c;包括在北京等20个城市试点实行换发补发出入境证件的“全程网办”&#xff0c;该举措对于访问学者、博士后研究人员及联合培养博士都是利好消息。故知识人网小编转载发布。 为更…

自动语音识别

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

恭喜发财!东方第一 MEME 拥抱符文

第 431 号符文 HOPE•YOU•GET•RICH &#x1f9e7;&#xff0c;是 Omnity 首个支持的跨链 Runes 资产&#xff0c;也是TG群里红包小程序支持的第一个 Runes 资产。 大家可以在 Omnity 的 TG 群和 RunesCC 的 TG 群里&#xff0c;不定时的抢到符文红包。 Omnity TG&#xff1a;…

Git系列:git push (-u) 与 git branch (-u)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

W801学习笔记二十二:英语背单词学习应用——下

续上篇&#xff1a; W801学习笔记二十一&#xff1a;英语背单词学习应用——上 五、处理用户交互 由于英语也是采用了和唐诗一样的《三分钟限时挑战》《五十题竞速挑战》《零错误闯关挑战》&#xff0c;所以用户交互的逻辑和唐诗是一样的。所以&#xff0c;我们抽一个基类&a…

热敏电阻怎么进行性能测试?并以LabVIEW为例进行说明

过程也可用于执行热敏电阻测量。RTD和热敏电阻遵循非常相似的功能原理&#xff0c;测量步骤与下面提供的步骤相同。有关热敏电阻的更多信息&#xff0c;请参阅本文档。 查找设备引脚排列 在连接任何信号之前&#xff0c;请找到您的设备引脚排列。 打开NI MAX并展开设备和接口。…

ETLCloud工具怎么实现多流SQL实时运算?

多流SQL实时运算的特点和应用场景 多流SQL实时运算是一种先进的数据处理技术&#xff0c;它在大数据处理领域中扮演着至关重要的角色&#xff0c;尤其是在需要对多个数据流进行实时分析和处理的应用场景中。该技术结合了SQL&#xff08;结构化查询语言&#xff09;的易用性和流…

【计算机毕业设计】基于SSM++jsp的网络游戏公司官方平台系统【源码+lw+部署文档+讲解】

目录 第1章 绪论 1.1 课题背景 1.2 课题意义 1.3 研究内容 第2章 开发环境与技术 2.1 MYSQL数据库 2.2 JSP技术 2.3 SSM框架 第3章 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统流程 3.2.1 操作流程 3.2.2 登录流程 3.2.3 删除信息流…

Redis 入坑基本指南

引言 本指南将帮助您了解如何安装、配置和基本使用 Redis。Redis 是一款开源的高性能键值存储系统&#xff0c;可用于缓存、数据库、消息中间件等多种用途。 1. 安装 Redis a. 下载 Redis&#xff1a; 可以从 Redis 官方网站&#xff08;https://redis.io&#xff09;下载最…

GORM 与 MySQL(一)

GORM 操作 Mysql 数据库&#xff08;一&#xff09; 温馨提示&#xff1a;以下关于 GORM 的使用&#xff0c;是基于 Gin 框架的基础上&#xff0c;如果之前没有了解过 Gin 可能略微困难。 GORM 介绍 GORM 是 Golang 的一个 orm 框架。简单说&#xff0c;ORM 就是通过实例对象…

Ubuntu18.04设置SSH密钥登录

我们一般使用 VSCode 、MobaXterm、PuTTY等 SSH 客户端来远程管理 Linux 服务器。但是&#xff0c;一般的密码方式登录&#xff0c;容易有密码被暴力破解的问题。所以&#xff0c;一般我们会将 SSH 的端口设置为默认的 22 以外的端口&#xff0c;或者禁用 root 账户登录。但是即…

解决ImportError: cannot import name ‘xxx‘ from partially initialized module xxx

python项目中某个文件名与需要引入的module中的文件名相同时&#xff0c;可能出现循环引用的情况&#xff0c;此时会报错ImportError: cannot import name ‘xxx‘ from partially initialized module xxx。 所以把项目文件中涉及 报错内容的python文件名 修改即可。 如我的…

剧本杀小程序,为商家带来更多收益

剧本杀作为一种社交类游戏&#xff0c;关注度越来越高&#xff0c;目前&#xff0c;市场上剧本杀依然呈现上升发展趋势。 不过当下&#xff0c;在剧本杀市场中&#xff0c;大部分商家都开始使用小程序管理运营剧本杀。相对于线下剧本杀&#xff0c;线上剧本杀小程序便于商家管…
最新文章