代码随想录算法训练营第46天| Leetcode 139.单词拆分、卡码网 56. 携带矿石资源(附带多重背包的基本解法和优化)

文章目录

    • Leetcode 139.单词拆分
    • 卡码网 56. 携带矿石资源
      • 方法一: 分组转化成01背包
      • 方法二: 转化成01背包+完全背包(基于方法一的小优化)
      • 方法三: 二进制优化(优化了方法一的分组方式)

Leetcode 139.单词拆分

题目链接:Leetcode 139.单词拆分
题目描述: 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
思路: 由于单词可以重复使用,并且要求正确顺序,因此可以联想到完全背包中的求排列问题。

  • dp[i] : 字符串长度为i的话,dp[i]true,表示可以拆分为一个或多个在字典中出现的单词。
  • 递推公式:如果截取的单词[i - sz, sz]等于word并且dp[i - sz] == true,则dp[i] = true
  • 初始化:dp[0] = ture
  • 遍历顺序:先遍历背包,后遍历物品。

代码如下:

class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {vector<bool> dp(s.size() + 5);dp[0] = true;// 完全背包求排列问题for (int i = 1; i <= s.size(); i++) // 先遍历背包for (auto& word : wordDict) {   // 后遍历物品int sz = word.size();// 首先背包容量要大于单词大小// 其次,如果截取的单词[i-sz,sz]等于word并且dp[i-sz]==true,则dp[i]=trueif (i >= sz && s.substr(i - sz, sz) == word) {dp[i] = dp[i] || dp[i - sz];}}return dp[s.size()];}
};
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度 O ( n ) O(n) O(n)

卡码网 56. 携带矿石资源

题目链接:卡码网 56. 携带矿石资源
在这里插入图片描述

思路: 这道题是多重背包的模板题。首先观察多重背包的特点是每件物品的数量不相同,其余的条件和01背包和完全背包很像,那能不能将其转化成它们呢?答案是可以。

方法一: 分组转化成01背包

由于多重背包每件物品数量不相同,不知道一种占用空间 w ,价值为 v,数量为 s 的物品该拿多少件,因此我们就把该拿多少件枚举一下,假设为 k1 <= k <= s ,然后我们就把问题看成仅有一件的占用空间k * w ,价值为k * v的物品该不该拿。
代码如下:

#include <iostream>
#include <vector>using namespace std;const int N = 1e4 + 5;int w[N],v[N],k[N];
int dp[N];
int n,m;int main()
{cin >> n >> m;//背包大小和物品数量for(int i = 1; i <= m; i ++ ) cin >> w[i];for(int i = 1; i <= m; i ++ ) cin >> v[i];for(int i = 1; i <= m; i ++ ) cin >> k[i];//转化成01背包for(int i = 1; i <= m; i ++ )//遍历物品for(int j = n; j >= w[i]; j -- )//遍历背包//遍历个数for(int num = 1; num <= k[i] && j - num * w[i] >= 0; num ++ )dp[j] = max(dp[j], dp[j - num * w[i]] + num * v[i]);cout << dp[n];return 0;
}
  • 时间复杂度 O ( n 3 ) O(n^3) O(n3)
  • 空间复杂度 O ( n ) O(n) O(n)

方法二: 转化成01背包+完全背包(基于方法一的小优化)

我们发现,如果k个物品的总重量大于等于背包的最大容量,说明在当前大小的背包容量下,该物品有无限个和只有k个的效果是相同的,这部分物品就转化成了完全背包问题。

为什么要转换成完全背包?我们看方法一转化成01背包是需要枚举一下拿多少件的,而转化为完全背包是不需要枚举多少件的,可以拿我们就拿,所以在时间上会有一些优化。(尽管时间复杂度不变)

代码如下:

#include <iostream>
#include <vector>using namespace std;const int N = 1e4 + 5;int w[N],v[N],k[N];
int dp[N];
int n,m;int main()
{               cin >> n >> m;//背包大小和物品数量for(int i = 1; i <= m; i ++ ) cin >> w[i];for(int i = 1; i <= m; i ++ ) cin >> v[i];for(int i = 1; i <= m; i ++ ) cin >> k[i];//转化成01背包+完全背包for(int i = 1; i <= m; i ++ )//遍历物品{if(k[i] * w[i] >= n)//如果不能把某个物品全部一次性装下,则可转化为完全背包{for(int j = w[i]; j <= n; j ++ )dp[j] = max(dp[j], dp[j - w[i]] + v[i]);}else{for(int j = n; j >= w[i]; j -- )//遍历背包//遍历个数for(int num = 1; num <= k[i] && j - num * w[i] >= 0; num ++ )dp[j] = max(dp[j], dp[j - num * w[i]] + num * v[i]);}}cout << dp[n];return 0;
}
  • 时间复杂度 O ( n 3 ) O(n^3) O(n3)
  • 空间复杂度 O ( n ) O(n) O(n)

方法三: 二进制优化(优化了方法一的分组方式)

我们发现利用从 1 1 1 2 2 2 4 4 4…… 2 n 2^n 2n可以枚举出 [ 0 , 2 n − 1 ] [0,2^n-1] [0,2n1]的所有数字(利用等比数列求和可以证明),因此可以不用像方法一那样依次枚举每个数字,本题数据范围是 1 0 4 10^4 104,而 2 13 = 8192 2^{13}=8192 213=8192,也就是每个数字最多分为 14 14 14组即可,不必依次枚举k,大大降低了时间复杂度。
代码如下:

#include <iostream>
#include <vector>using namespace std;const int N = 1e4 + 5;
const int M = 13 * N + 5;//根据数据范围,一个数最多拆13次,因此要比原来多开一些空间
int w[N],v[N],k[N];
int W[M],V[M];//用来记录分组之后的物品重量和价值
int dp[N];
int n,m;int main()
{               cin >> n >> m;//背包大小和物品数量for(int i = 1; i <= m; i ++ ) cin >> w[i];for(int i = 1; i <= m; i ++ ) cin >> v[i];for(int i = 1; i <= m; i ++ ) cin >> k[i];//将物品数量按照(1,2,4...2^n)分组,然后转化为01背包(优化了遍历次数)int cnt = 0;//记录物品数量for(int i = 1; i <= m; i ++ )//分组过程{int num = 1;while(num <= k[i]){cnt ++ ;W[cnt] = num * w[i];V[cnt] = num * v[i];k[i] -= num;num *= 2;}if(k[i])//剩下的直接放入{cnt ++ ;W[cnt] = k[i] * w[i];V[cnt] = k[i] * v[i];}}//01背包过程for(int i = 1;i <= cnt; i ++ )for(int j = n; j >= W[i]; j -- ){dp[j] = max(dp[j], dp[j - W[i]] + V[i]);}//输出cout << dp[n];return 0;
}
  • 时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
  • 空间复杂度 O ( n ) O(n) O(n)

总结: 遇到一个新问题首先我们可以思考是否可以利用已有知识来解决,就像多重背包的解法就是基于01背包和完全背包。

最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pygame教程01:初识pygame游戏模块

Pygame是一个用于创建基本的2D游戏和图形应用程序。它提供了一套丰富的工具&#xff0c;让开发者能够轻松地创建游戏和其他图形应用程序。Pygame 支持许多功能&#xff0c;包括图像和声音处理、事件处理、碰撞检测、字体渲染等。 Pygame 是在 SDL&#xff08;Simple DirectMed…

Effective objective-c-- 内存管理

Effective objective-c-- 内存管理 前言理解引用计数引用计数工作原理属性存取方法中的内存管理自动释放池保留环要点 以ARC简化引用计数使用ARC时必须遵循的方法和命名规则变量的内存管理语义ARC如何清理实例变量覆写内存管理方法要点 在dealloc方法中只释放引用并解除监听要点…

智能汽车加速车规级存储应用DS2431P+TR 汽车级EEPROM 存储器IC

DS2431PT&R是一款1024位1-Wire EEPROM芯片&#xff0c;由四页存储区组成&#xff0c;每页256位。数据先被写入一个8字节暂存器中&#xff0c;经校验后复制到EEPROM存储器。该器件的特点是&#xff0c;四页存储区相互独立&#xff0c;可以单独进行写保护或进入EPROM仿真模式…

实践航拍小目标检测,基于轻量级YOLOv7tiny开发构建无人机航拍场景下的小目标检测识别分析系统

关于无人机相关的场景在我们之前的博文也有一些比较早期的实践&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《deepLabV3Plus实现无人机航拍目标分割识别系统》 《基于目标检测的无人机航拍场景下小目标检测实践》 《助力环保河道水质监测&#xff0c;基于yolov…

C语言冒泡排序(高级版)

目录: 冒泡排序的原理 主函数 "冒泡排序函数" 比较函数 交换函数 最终输出 完整代码 冒泡排序的原理: 冒泡排序的原理是&#xff1a;从左到右&#xff0c;相邻元素进行比较。每次比较一轮&#xff0c;就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右…

Linux课程四课---Linux开发环境的使用(自动化构建工具-make/Makefile的相关)

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Linux之相对路径、绝对路径、特殊路径符

相对路径和绝对路径 cd /root/temp 绝对路径写法 cd temp 相对路径写法 1、绝对路径&#xff1a;以根目录为起点&#xff0c;描述路径的一种写法&#xff0c;路径描述以 / 开头。 2、相对路径&#xff1a;以当前目录为起点&#xff0c;路径描述无需以 / 开头。 特殊路径符 如…

基于Java的超市商品管理系统(Vue.js+SpringBoot)

目录 一、摘要1.1 简介1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 超市区域模块2.3 超市货架模块2.4 商品类型模块2.5 商品档案模块 三、系统设计3.1 用例图3.2 时序图3.3 类图3.4 E-R图 四、系统实现4.1 登录4.2 注册4.3 主页4.4 超市区域管理4.5 超市货架管理4.6 商品类型…

【踏雪无痕的痕五】——一年级数学题映射动态规划

目录 一、背景介绍三、过程1.那是什么样的一个数学题&#xff1f;2.动态规划是个啥&#xff1f;3.为啥联系到动态规划了&#xff1f;4.拿01背包算法做个小例子练练手吧5.感受 四、总结 一、背景介绍 小编发烧并发症一周了&#xff0c;这一周从最开始的轻飘飘找不到灵魂在哪里—…

数据结构c版(3)——排序算法

本章我们来学习一下数据结构的排序算法&#xff01; 目录 1.排序的概念及其运用 1.1排序的概念 1.2 常见的排序算法 2.常见排序算法的实现 2.1 插入排序 2.1.1基本思想&#xff1a; 2.1.2直接插入排序&#xff1a; 2.1.3 希尔排序( 缩小增量排序 ) 2.2 选择排序 2.2…

Leetcode热题100道

Leetcode热题100道 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是 枫度柚子&#x1f341;&#xff0c;Java摆烂选手&#xff0c;很高兴认识大家 &#x1f440; &#x1f4d5;CSDN/掘金/B站: 枫吹过的柚 &#x1f341; &#x1f525;如果感觉博主的文章还不错的话&a…