代码随想录算法训练营第三十八天打卡

       今天是动态规划的第三天,昨天的不同路径与整数分解的几道题目大家理解得如何?如果有疑问大家还是多去想想dp数组究竟是什么含义,还有我的状态转移是否正确,初始化是否正确,这一点很重要,今天的题目依旧是跑不出我们的动规五部曲,我们就一起走进我们今天的题目。

第一部分 0-1背包问题的理论基础

       今天我们就进入了动态规划里面一个非常重要的问题:背包问题,首先我们要来了解一下背包问题是什么,它可以解决什么样的问题以及什么情况下我们就可以判定这是一个背包问题,首先我们来看背包问题是什么:

      首先我要先告诉大家一句就是我们Leetcode上是没有单纯的考察0-1背包的题目的。当然我可以给大家补充几道,一个是蓝桥云课上可以找到另一个就是卡码网上会有,那我们重点需要理解0-1背包问题和完全背包问题,尤其是0-1背包问题这个及其重要,假设我们有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。这就是0-1背包问题注意我们的每一件物品只能使用一次,但是如果是完全背包问题那么物品数量是无限的。这点大家要区分清楚。

        接下来是我们如何考虑背包问题:每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量。但是这样使用回溯法太慢了很可能会导致超时,那么我们就需要使用动态规划的思想,举个例子:

dou

       这个当然大家很轻松就看出来了结果就是35,我们有两种选法一个是选择物品0与物品1,一种是选择物品2,很明显我们在达到最大重量之前我们可以获得最大价值,那我们还需要使用动规五部曲来解决背包问题:

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

      这个在动规五部曲里面是很重要的,因为有两个维度需要分别表示:物品 和 背包容量,因此我们需要使用二维数组来表示,dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少,这个很重要的,我们对于每一件产品都存在两种情况就是取或者不取。这点大家要清楚。

2. 确定递推公式

      这个是背包问题很容易出错的地方,其实主要原因还是没搞清楚dp数组的含义,dp[1][4] 我们存在两种情况,分别是:

  1. 放物品1
  2. 还是不放物品1

       因此我们就根据上面给出的例题来看,dp[1][4] = max(dp[0][4], dp[0][1] + 物品1 的价值),我们要取的是最大价值,第一种就是我们不选物品1,那这样目前的最大价值就是前面的物品带来的,如果我们选了物品1,那么我们背包的剩余容量要减去物品1的重量最后不要忘记加上物品1的价值,因为我们的dp数组表示的本就是最大价值。

3. dp数组如何初始化

     关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱

     首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。还有一个dp[0][j]需要考虑,这个其实要取决于背包容量了,如果j >= value[0]的话那我们可以装下这件物品所以初始化为value[0],如果小于的话就是0表示装不下物品

     这里我给出大家初始化的代码:

vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];
}

         为什么要倒序遍历就是考虑到了那个可以装下第一件物品的事情。

4. 确定遍历顺序

     先遍历 物品还是先遍历背包重量呢?其实都是可以的,我先给出先遍历物品,然后遍历背包重量的代码。这里两种代码都提供给大家:

// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品for(int j = 0; j <= bagweight; j++) { // 遍历背包容量if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}
}
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量for(int i = 1; i < weight.size(); i++) { // 遍历物品if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}
}

        当然我更偏向于第一种先遍历物品再遍历背包的剩余容量,那么最后大家就需要自己打印一下我们的dp数组便于大家理解,也可以看看自己的状态转移方程究竟对不对。

第二部分 0-1背包问题的例题卡码网第46题

       这里我就不多讲了,大家应该可以看出这是一个背包问题,不就是我们这里变成了携带的研究材料取得最大价值,我就直接给出大家ACM模式的代码:

#include <bits/stdc++.h>
using namespace std;int main() {int n, bagweight;// bagweight代表行李箱空间cin >> n >> bagweight;vector<int> weight(n, 0); // 存储每件物品所占空间vector<int> value(n, 0);  // 存储每件物品价值for(int i = 0; i < n; ++i) {cin >> weight[i];}for(int j = 0; j < n; ++j) {cin >> value[j];}// dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));// 初始化, 因为需要用到dp[i - 1]的值// j < weight[0]已在上方被初始化为0// j >= weight[0]的值就初始化为value[0]for (int j = weight[0]; j <= bagweight; j++) {dp[0][j] = value[0];}for(int i = 1; i < weight.size(); i++) { // 遍历科研物品for(int j = 0; j <= bagweight; j++) { // 遍历行李箱容量if (j < weight[i]) dp[i][j] = dp[i - 1][j]; // 如果装不下这个物品,那么就继承dp[i - 1][j]的值else {dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}}}cout << dp[n - 1][bagweight] << endl;return 0;
}

第三部分 背包问题的压缩与优化

      在这里我们可以将二维的dp数组优化成一维的dp数组,其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);但是大家要注意我们如果压缩后了新的数组的含义就会变成容量为j的背包,所背的物品价值可以最大为dp[j]。这样的话我们的状态转移方程就可以变为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])。

     dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j],那么dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是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]);}
}

      在这里大家可以看到我们的背包容量是倒序遍历的,这个是为什么其实是为了保证物品i只被放入一次,如果正序遍历我们就会导致那么物品0就会被重复加入多次,这里可以举一个例子助于大家理解:

物品0的重量weight[0] = 1,价值value[0] = 15

如果正序遍历:dp[1] = dp[1 - weight[0]] + value[0] = 15    dp[2] = dp[2 - weight[0]] + value[0] = 30

这样的话我的物品0不就放了两次了,这就不符合0-1背包的要求了。

如果是倒序遍历的话:我们就会先计算dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)

接下来就是dp[1] = dp[1 - weight[0]] + value[0] = 15  这样就可以避免出现物品放多次的情况。

       关于背包问题我就给大家讲解这么多,其实这只是背包问题的理论基础,我们还是要去看力扣上涉及到背包问题的具体题目。

开始力扣上的题目第416题分割等和子集

      其实很可能是我算法思维在训练营一个多月变强了,我一看到题目就大致猜出来了要用背包问题的思路来解,不是因为这道题目在背包问题专栏里面,我们来看一下题目的要求:

        这道题目是我们把数组分开,然后看看能不能把这个数组分成两个和的数组,其实这个大家是否可以想清楚就是一个背包问题,就是看看我们能不能达到那个最大价值,那个最大价值就是原先数组和的一半,其实数组里的每一个数字就相当于我们理论基础里面的物品的价值,这里大家要注意有一点就是在这里我们物品的价值与质量其实是等值的,只要想清楚了这一点再加上我们对背包问题已经比较熟悉那么解决这道题目就不难了,我们一起看思路:

        首先我们要先求出原先数组的和,然后我们使用一个变量存储最大价值,也就是和的一半,当然在这里我们进行一步剪枝的操作,如果我们的和是奇数那么就不可能分成两个和相等的数组,这点大家应该注意到,接下来就是我们动态规划的步骤了,同样我们还是定义dp数组,当然初始化很简单dp[0] = 0,我们dp数组表示的是目前i个数的和,当然我们就需要在遍历的时候考虑对于每一个数我们是否选择它,我们就可以写出以下的代码:

class Solution {
public:bool canPartition(vector<int>& nums) {int sum = 0;vector<int> dp(10001, 0);for (int i = 0; i < nums.size(); ++i) sum += nums[i];if (sum % 2 == 1) return false; //奇数不可能凑出两个子集的元素和相等int target = sum / 2;dp[0] = 0;//0-1背包问题但在这里比较特殊的是物品的重量与价值其实是相等的for (int i = 1; i < nums.size(); ++i){for (int j = target; j >= nums[i]; --j){dp[j] = max(dp[j],dp[j - nums[i]] + nums[i]);}}//看看这个背包能不能装满return dp[target] == target;}
};

       下面都是背包问题的理论基础,我们还是先枚举物品再枚举背包容量,当然我们背包容量需要倒序遍历,这是为了保证每一个物品使用一次,最后我们看看dp[target] == target是否正确,就是我们能不能凑出原数组和的一半这个数,如果可以就返回true否则就是false。

今日总结

      今天的背包问题尤其是0-1背包问题其实是很重要的,理解背包问题的二维数组与一维数组的思路,注意遍历顺序就可以了,今天的题目其实大家只看看出考察的就是背包问题估计就不难了,好的,我们今天就分享到这里,我们明天见!

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

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

相关文章

【全解析】EN18031标准下的SUM安全更新机制

在网络安全问题层出不穷的今天&#xff0c;无线电设备的安全更新是保障其正常运行、抵御威胁的核心环节。EN18031标准中的SUM&#xff08;Secure Update Mechanism&#xff09;安全更新机制&#xff0c;为无线电设备的软件更新安全筑牢了规范防线。接下来&#xff0c;让我们详细…

小学数学题批量生成及检查工具

软件介绍 今天给大家介绍一款近期发现的小工具&#xff0c;它非常实用。 软件特点与出题功能 这款软件体积小巧&#xff0c;不足两兆&#xff0c;具备强大的功能&#xff0c;能够轻松实现批量出题。使用时&#xff0c;只需打开软件&#xff0c;输入最大数和最小数&#xff0c…

【LeetCode 热题 100】动态规划 系列

&#x1f4c1; 70. 爬楼梯 状态标识&#xff1a;爬到第i层楼梯时&#xff0c;有多少种方法。 状态转移方程&#xff1a;dp[i] dp[i-1] dp[i-2]&#xff0c;表示从走一步和走两步的方式。 初始化&#xff1a;dp[1] 1 , dp[2] 2。 返回值&#xff1a;dp[n]&#xff0c;即走到…

Linux系统篇——文件描述符FD

&#x1f9e0; Linux 文件描述符&#xff08;File Descriptor&#xff09;详解与学习指南 一、什么是文件描述符&#xff08;fd&#xff09; 在 Linux 中&#xff0c;一切皆文件&#xff08;everything is a file&#xff09;&#xff0c;包括普通文件、目录、套接字&#xff…

CSS- 2.1 实战之图文混排、表格、表单、学校官网一级导航栏

本系列可作为前端学习系列的笔记&#xff0c;代码的运行环境是在HBuilder中&#xff0c;小编会将代码复制下来&#xff0c;大家复制下来就可以练习了&#xff0c;方便大家学习。 HTML系列文章 已经收录在前端专栏&#xff0c;有需要的宝宝们可以点击前端专栏查看&#xff01; 系…

文件同步2

请大家思考如何使用scp命令去解决这个问题。 有两种思路&#xff1a; 第一种&#xff1a;三个文件一个一个去拷贝。缺点是操作麻烦&#xff0c;要逐一操作。 第二种&#xff1a;重新把A上的conf拷贝到B上。缺点是会重复拷贝文件1&#xff0c;2&#xff0c;3&#xff0c;4。 …

Maven使用详解:Maven的概述(二)

一、核心定义与功能 Maven是由Apache软件基金会开发的开源项目管理工具&#xff0c;专为Java项目设计&#xff0c;主要用于自动化构建、依赖管理和项目标准化。其核心功能包括&#xff1a; 依赖管理&#xff1a;通过pom.xml文件声明依赖库&#xff0c;自动从中央仓库下载并管…

大疆无人机自主飞行解决方案局限性及增强解决方案-AIBOX:特色行业无人机巡检解决方案

大疆无人机自主飞行解决方案局限性及增强解决方案-AIBOX&#xff1a;特色行业无人机巡检解决方案 大疆无人机是低空行业无人机最具性价比的产品&#xff0c;尤其是大疆机场3的推出&#xff0c;以及持续自身产品升级迭代&#xff0c;包括司空2、大疆智图以及大疆智运等专业软件和…

leetcode:58. 最后一个单词的长度(python3解法)

难度&#xff1a;简单 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hello World"…

Ubuntu24.04 安装 5080显卡驱动以及cuda

前言 之前使用Ubuntu22.04版本一直报错,然后换了24.04版本才能正常安装 一. 配置基础环境 Linux系统进行环境开发环境配置-CSDN博客 二. 安装显卡驱动 1.安装驱动 按以下步骤来&#xff1a; sudo apt update && sudo apt upgrade -y#下载最新内核并安装 sudo add…

Github 2025-05-15 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2025-05-15统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Python项目1Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:27…

操作系统-锁/内存/中断/IO

文章目录 锁自旋锁互斥锁悲观锁和乐观锁 内存管理物理/虚拟内存页表段表虚拟内存布局写时复制copy on writebrk&#xff0c;mmap页面置换算法 中断中断分类中断流程 网络I/OI/O模型服务器处理并发请求 锁 自旋锁 自旋锁是一种基于忙等待&#xff08;Busy-Waiting&#xff09;…

校园一卡通安全策略研究调研报告

目录 一、研究背景 二、问题分析 三、解决方案 1.系统功能整合 2.用户体验优化 3.安全性能提升 4.代码说明 ①ConfigController.java ②FileController.java ③SushedianfeiController.java ④UserService.java ⑤XiaoyuankachongzhiController.java ⑥其他代码 …

EWOMAIL

1、错误 Problem: problem with installed package selinux-policy-targeted-3.14.3-41.el8.noarch package fail2ban-server-1.0.2-3.el8.noarch requires (fail2ban-selinux if selinux-policy-targeted), but none of the providers can be installed - package fail2ban-…

强化学习算法实战:一个例子实现sarsa、dqn、ddqn、qac、a2c、trpo、ppo

简介 在学习强化学习算法&#xff1a;sarsa、dqn、ddqn、qac、a2c、trpo、ppo时&#xff0c;由于有大量数据公式的推导&#xff0c;觉得十分晦涩&#xff0c;且听过就忘记了。 但是当把算法应用于实战时&#xff0c;代码的实现要比数据推导要直观很多。 接下来通过不同的算法实…

《数据库原理》部分习题解析1

《数据库原理》部分习题解析1 1. 名词解释 &#xff08;1&#xff09;关系&#xff08;2&#xff09;属性&#xff08;3&#xff09;域&#xff08;4&#xff09;元组&#xff08;5&#xff09;码&#xff08;6&#xff09;分量&#xff08;7&#xff09;关系模式 &#xff0…

20250515通过以太网让VLC拉取视熙科技的机芯的rtsp视频流的步骤

20250515通过以太网让VLC拉取视熙科技的机芯的rtsp视频流的步骤 2025/5/15 20:26 缘起&#xff1a;荣品的PRO-RK3566适配视熙科技 的4800W的机芯。 1080p出图预览的时候没图了。 通过105的机芯出图确认 荣品的PRO-RK3566 的硬件正常。 然后要确认 视熙科技 的4800W的机芯是否出…

AI 赋能防艾宣传:从创意到实践,我的 IP 形象设计之旅

在数字技术飞速发展的今天&#xff0c;如何让严肃的健康传播变得更有温度、更具吸引力&#xff1f;作为一名参与防艾宣传实践的学生&#xff0c;我尝试通过 AI 工具构建专属 IP 形象&#xff0c;让防艾知识从 "被动接受" 转化为 "主动探索"。这篇文章将分享…

机器学习笔记2

5 TfidfVectorizer TF-IDF文本特征词的重要程度特征提取 (1) 算法 词频(Term Frequency, TF), 表示一个词在当前篇文章中的重要性 逆文档频率(Inverse Document Frequency, IDF), 反映了词在整个文档集合中的稀有程度 (2) API sklearn.feature_extraction.text.TfidfVector…

MYSQL 子查询

标量子查询 #标量子查询 #1.查询“研发部”的所有员工信息 # a.查询“研发部”部门id select id from dept where name研发部;#b.根据销售部部门id&#xff0c;查询员工信息 select * from emp where dept_id(select id from dept where name研发部);#2.查询在王金彪入职之后…