代码随想录第十八天|动态规划(2)

news2024/11/23 18:59:36

目录

01背包问题——二维数组

01背包问题——一维数组

LeetCode 416. 分割等和子集

LeetCode 1049. 最后一块石头的重量 II

LeetCode 494. 目标和

LeetCode 474. 一和零

总结


01背包问题——二维数组

有n件物品和一个最多能放入重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能使用一次,求解将哪些物品装入背包里物品价值总和最大。

  • 确定dp数组以及下标含义

对于背包问题,第一种写法为使用二维数组,即dp[i][j]表示从下标为[0-i]的物品里随意取,放进容量为[j]的背包,价值总和最大是多少。

  • 确定递推公式

dp[i][j]有两个方向能推出来:首先就是,不放物品i,当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面的价值相同。即dp[i][j] = dp[i-1][j];其次就是,放入物品i,此时放入物品i之前背包里面的最大价值为dp[i-1][j-weight[i]],那么当放入物品i之后,该背包的最大价值为dp[i-1][j-weight[i]] + value[i]。

所以递推公式应为:dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])。

  • dp数组的初始化

如果背包容量j为0的话,无论是选取哪些物品,背包价值总和一定为0。即dp[i][0] = 0;

由递推公式可以看出来,物品i的状态是由i-1推出来的,所以dp[0][j]一定要初始化。即存放编号0物品的时候,各个容量的背包所能存放的最大价值。那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。

其他的地方就可以随便初始化了。

  • 确定遍历顺序

背包里面有两种遍历顺序,先物品后重量,先重量后物品。先遍历物品更好理解一点。

// 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]);
    }
}
  • 举例验证

这一步就自己在本子上一遍就可以了。

01背包问题——一维数组

通过上面二维数组的递推公式可以看出,其实每个数组在更新的时候只需要两层数据就行了,那么在进行递推的时候,可以把上一层的数据拷贝到当前层,在当前层进行计算就OK了。

  • dp数组以及下标的含义

这里使用到了一维数组,即dp[j],其代表的含义是容量为j的背包所具有的最大价值。

  • 递推公式的确定

dp[j]和dp[i][j]一样有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值。所以递推公式为:dp[j] = max(dp[j], dp[j - weight[i]) + value[i])。

  • dp数组的初始化

dp数组下标为0的时候,初始化为0,这一点跟二维数组一样。其他下标,在价值全为正的时候初始化0;有负数参与进来的时候就取负无穷大。

  • dp数组的遍历顺序

在二维dp遍历的时候,是先物品后重量。这里也是,但有些许变化。重量这一项应该为倒序遍历。倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!

  • 举例验证

跟上述一样就自己推一推就行了。

LeetCode 416. 分割等和子集

题目链接:LeetCode 416. 分割等和子集

思想:要把一个集合分割成两个元素和相等的子集,这个问题也可以转化成01背包问题。每个数的值就是其价值和重量,背包的容量就是集合元素总和的一半,因为只用找出一半就行了。套入动态规划的公式。

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

这里的dp数组我采用一维数组dp[j],即容量为j的背包所含的最大价值。其中每个物品的价值和重量都是其本身的值。

  • 确定递推公式

递推公式也是背包一维数组的递推公式,即dp[j] = max(dp[j], dp[j - weight[i]] + value[i])。

  • dp数组的初始化

这道题dp数组中每个元素的值都挺好确认,因为没有负数元素,所以初始化为0。但dp数组的大小不好确认,因为j为重量的含义,在本题中也代表着目前子集和数的大小。根据题目给出的提醒,数组不超过200个元素,每个元素不超过100,所以nums数组最大子集和为200*100=20000,因为这里只需要确认一半,所以dp数组的大小可以设置为10001。

  • dp数组遍历顺序

遍历顺序就跟上述一样

代码如下:

    bool canPartition(vector<int>& nums) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int target = 0;
        if (sum % 2 == 1) return false;
        target = sum / 2;
        vector<int> dp(10001, 0);
        for (int i = 0; i < nums.size(); i++) {
            for (int j = target; j >= nums[i]; j--) {
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        if (target == dp[target]) return true;
        return false;
    }

时间复杂度:O(n^2),空间复杂度:O(n)。

LeetCode 1049. 最后一块石头的重量 II

题目链接:LeetCode 1049. 最后一块石头的重量 II

思想:本题跟上题十分相似,可以借助上一题的想法,两块石头互相比,那么就可以更改为找出两个总和差不多的子集合,然后作差,得到最小的可能重量。思想跟上题差不多就不过多赘述了。这里注意还是dp数组的大小取舍。

代码如下:

    int lastStoneWeightII(vector<int>& stones) {
        int sum = accumulate(stones.begin(), stones.end(), 0);
        int target = sum / 2;
        vector<int> dp(1501, 0);
        for (int i = 0; i < stones.size(); i++) {
            for (int j = target; j >= stones[i]; j--) {
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return (sum - dp[target]) - dp[target];
    }

时间复杂度:O(n^2),空间复杂度:O(n)。

LeetCode 494. 目标和

题目链接:LeetCode 494. 目标和

思想:本题在回溯算法的时候做过一次,本次可以用01背包的问题再做一次。假设sum是数组内所有元素的总和,设所有加法加起来的总和为x,那么减法为sum - x。根据题意,有x-(sum - x) =target。 x = (target + sum) / 2。此时就可以看出,可以把x设为背包的容量,dp[j]代表着容量为j的背包,满足target的办法有多少种。然后再套入01背包的公式里面。需要注意的是,本题的递推公式和初始化有些许不同。关于递推公式,只要搞到nums[i],凑成dp[j]就有dp[j-nums[i]]种方法。那么终于有多少方法呢,就是把所有dp[j - nums[i]]累加起来。即dp[j] += dp[j - nums[i]]。而关于初始化,根据递推公式可以看出,如果初始化dp[0]为0,那么所有的结果都为0。所以这里应该要把dp[0]初始化为1。其他的就跟上述大差不差。

代码如下:

    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if ((sum + target) % 2 == 1) return 0;
        if (abs(target) > sum) return 0;
        int x = (sum + target) / 2;
        vector<int> dp(x + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = x; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[x];
    }

时间复杂度:O(n^2),空间复杂度:O(n)。

LeetCode 474. 一和零

题目链接:LeetCode 474. 一和零

思想:本题相当于是01背包问题,只不过背包会有两个维度,m和n,而不同长度的字符串就是不同大小的待装物品。关于dp数组的含义,最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。而递推公式的话,dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。然后我们在遍历的过程中,取dp[i][j]的最大值。所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

物品就是strs里的字符串,背包容量就是题目描述中的m和n。

代码如下:

    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0));
        for (string str : strs) {
            int one = 0, zero = 0;
            for (char c : str) {
                if (c == '0') zero++;
                else one++;
            }
            for (int i = m; i >= zero; i--) {
                for (int j = n; j >= one; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zero][j - one] + 1);
                }
            }
        }
        return dp[m][n];
    }

时间复杂度:O(n^3),空间复杂度:O(n^2)。

总结

动态规划中的01背包问题,最最最最最最主要的就是要把问题抽象成01背包问题,什么是背包,什么是物品,物品的重量是什么,物品的价值又是什么。之后就好做了。

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

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

相关文章

web基础之CSS

web基础之CSS 文章目录 web基础之CSS一、CSS简介二、基本用法2、CSS应用方式2.1 行内样式2.2内部样式2.3外部样式 三、选择器1、标签选择器2、类选择器3、ID选择器4、选择器的优先级 四、常见的CSS属性1、字体属性2、文本属性3、背景属性4、表格属性5、盒子模型的属性6、定位 总…

LabVIEW激光主动探测系统

开发了一种基于LabVIEW的高性能激光主动探测控制与处理系统的设计与实现。该系统充分利用了LabVIEW的多线程和模块化设计优势&#xff0c;提供了一套功能完整、运行高效且稳定的解决方案&#xff0c;适用于高精度激光探测领域。 项目背景 激光主动探测技术利用激光作为主动光源…

dp专题(二)

洛谷&#xff1a;B3626 跳跃机器人 题目描述 地上有一排格子&#xff0c;共 nn 个位置。机器猫站在第一个格子上&#xff0c;需要取第 nn 个格子里的东西。 机器猫当然不愿意自己跑过去&#xff0c;所以机器猫从口袋里掏出了一个机器人&#xff01;这个机器人的行动遵循下面…

【面试题】【C语言】寻找两个正序数组的中位数

寻找两个正序数组的中位数 仅供学习 题目 算法时间复杂度 二分查找算法&#xff0c;时间复杂度为 O(log(min(m, n)))&#xff0c;其中 m 和 n 分别是两个数组的长度。 子函数 查找两个数字的最大值 int max(int a, int b) {return a > b ? a : b; }查找两个数字的最小…

ubuntu20.04搭建RUST开发环境并与C语言交互

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 ubuntu20.04搭建RUST开发环境并与C语言交互 前言开战一、确认环境版本二、环境搭建三、hello world&#xff01;四、跟c语言进行交互1.rust调用C静态库2.C调用rust库 总结参考…

LDR6500:小封装,易设计外围简单OTG数据+充电实现原理

移动设备的普及与功能日益丰富的今天&#xff0c;OTG&#xff08;USB On-The-Go&#xff09;转接器作为连接移动设备与外部设备的桥梁&#xff0c;其重要性不言而喻。而LDR6500&#xff0c;作为乐得瑞科技精心打造的一款USB-C DRP&#xff08;Dual Role Port&#xff0c;双角色…

vue3修改带小数点的价格数字:小数点的前后数字,要分别显示成不同颜色和大小!已经封装成组件了!

需求&#xff1a; 修改带小数点的价格数字&#xff1a;小数点的前后数字&#xff0c;要分别显示成不同颜色和大小&#xff01;已经封装成组件了&#xff01; 效果&#xff1a; 前面大&#xff0c;后面小 代码&#xff1a; 组件&#xff1a; <!--修改小数点前后数字不同…

nodejs多版本随心切换-windows

nodejs多版本控制 1. 安装 nvm github下载地址 不需要卸载已安装的nodejs&#xff0c;安装时会让你选择nodejs的位置&#xff0c;可修改为你已经安装的路径&#xff0c;会自动搜索已安装版本&#xff0c;并进行弹窗询问&#xff0c;选择托管即可 2. 修改配置文件 在 nvm 安装…

全网最适合入门的面向对象编程教程:30 Python的内置数据类型-object根类

全网最适合入门的面向对象编程教程&#xff1a;30 Python 的内置数据类型-object 根类 摘要&#xff1a; 在 Python 中&#xff0c;所有的类都直接或间接继承自一个根类&#xff0c;这个根类是Object。Object类是 Python 中所有新式类的基础类&#xff0c;在 Python 的类层次结…

Docker安装Nacos及动态配置

文章目录 1.安装Nacos1.拉取镜像2.启动Nacos3.开启8848和9848端口1.88482.9848 4.访问nacos1.网址 http://guest:8848/nacos/ 2.Nacos动态配置&#xff08;无法实现bean动态加载&#xff09;1.新建一个配置&#xff0c;使其成为动态的2.引入Nacos依赖3.application.yml配置Naco…

常见病症之中医药草马齿苋

常见病症之中医药草马齿苋 1. 源由2. 马齿苋植物描述药用部分主要成分药理作用使用方法注意事项 2. 常用方剂2.1 马齿苋汤2.2 马齿苋粥 3. 马齿苋的奇效具体应用实例 4. 湿疹方剂4.1 常见方剂内服方剂加减调整外用方剂 4.2 加“马齿苋”内服方剂加减调整外用方剂 4.3 注意事项 …

【Spring Boot】配置 Spring Security

配置 Spring Security 1.继承 WebSecurityConfigurerAdapter2.配置自定义策略3.配置加密方式3.1 BCrypt 加密 4.自定义加密规则5.配置多用户系统5.1 构建 UserDetailsService 用户信息服务接口5.2 进行安全配置 6.获取当前登录用户信息的几种方式6.1 在 Thymeleaf 视图中获取6.…

第23集《大佛顶首楞严经》

请大家打开讲义第五十二页&#xff0c;癸八&#xff0c;约外道世谛对简显见性非因缘自然。 本经的修学特色&#xff0c;简单地讲&#xff0c;它是在处理生命的根本问题。就是当我们在行菩萨道的时候&#xff0c;我们会去布施、持戒、忍辱或者是禅定&#xff0c;在整个修学当中…

centos 8.5时间设置

编辑 chrony 配置文件 vim /etc/chrony.conf新增内容&#xff1a; server 210.72.145.44 iburst server ntp.aliyun.com iburst同时要注释一下&#xff1a;# pool 2.centos.pool.ntp.org iburst 重启chronydf服务 systemctl restart chronyd.service设置 chronyd 开机自启…

谷粒商城实战笔记-118-全文检索-ElasticSearch-进阶-aggregations聚合分析

文章目录 一&#xff0c;基本概念主要聚合类型 二&#xff0c;实战1&#xff0c;搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄&#xff0c;但不显示这些人的详情2&#xff0c;按照年龄聚合&#xff0c;并且请求每个年龄的平均薪资 Elasticsearch 的聚合&#xff0…

大屏宁德烟草第二版总结,批量导入

toFixed toFixed(x) 方法返回一个表示 numObj 的字符串&#xff0c;如果不该x&#xff0c;会进行四舍五入。 includes() includes() 方法用来判断一个数组是否包含一个指定的值&#xff0c;根据情况&#xff0c;如果包含则返回 true&#xff0c;否则返回 false。 批量导入…

饿了吗新版bxet

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(…

【Linux】网络编程_3

文章目录 十、网络基础5. socket编程socket 常见APIsockaddr结构简单的UDP网络程序 未完待续 十、网络基础 5. socket编程 socket 常见API // 创建 socket 文件描述符 (TCP/UDP, 客户端 服务器) int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服…

LLM大模型的书那么多,如何快速选到适合自己的那一本?来,我教你!

大模型的书这么多&#xff0c;该怎么选呢&#xff1f;今天就来教大家怎么快速地从众多大模型书中选到你想要的那一本&#xff01; 朋友们如果有需要这些大模型书 扫码获取~ &#x1f449;CSDN大礼包&#x1f381;&#xff1a;全网最全《LLM大模型书籍资源包》免费分享&#xf…

SpringBoot入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享

场景 作为一名Java开发者&#xff0c;SpringBoot已经成为日常开发所必须。 势必经历过从入门到自学、从基础到进阶、从学习到强化的过程。 当经历过几年企业级开发的磨炼&#xff0c;再回头看之前的开发过程、成长阶段发现确实是走了好多的弯路。 作为一名终身学习的信奉者…