代码随想录算法训练营第四十三天 | 填满背包有几种方法、背包有两个维度

news2024/11/26 12:27:09

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

文档讲解:代码随想录 (programmercarl.com)

视频讲解:动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II_哔哩哔哩_bilibili

状态:没想到。

思路

本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小这样就化解成01背包问题了

两堆重量不一定相等,比如总重量为X,

如果分开后两堆重量正好都为X/2,则为0;

如果分开后两堆重量为Y和Z(假设Y<Z),需要令Y尽可能大,才能在(Y+Z)固定的情况下,使得(Z-Y)尽可能小。由于Y<Z且Y+Z=X,所以Y小于等于X/2。为了令Y尽可能大,需要借助【01背包问题解法】求在背包容量为X/2时,收到的物品价值Y(各个石头重量和)尽可能大。

本题物品的重量为stones[i],物品的价值也为stones[i]。对应着01背包里的物品重量weight[i]和 物品价值value[i]。

动规五步曲:

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

    dp[j]表示容量为j的背包,最多可以背最大重量为dp[j]

    01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。

    相对于 01背包,本题中,石头的重量是 stones[i],石头的价值也是 stones[i] ,可以 “最多可以装的价值为 dp[j]” == “最多可以背的重量为dp[j]”

  2. 确定递推公式

    01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

  3. dp数组如何初始化

    既然 dp[j]中的j表示容量,那么最大容量(重量)是多少呢,就是所有石头的重量和。

    而我们要求的target其实只是最大重量的一半,所以计算出石头总重量 然后除2,得到dp数组的大小。

    接下来就是如何初始化dp[j]呢,因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。

  4. 确定遍历顺序

    如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

  5. 举例推导dp数组

    举例,输入:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:

在这里插入图片描述

最后dp[target]里是容量为target的背包所能背的最大重量。

那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。

在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的

那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for(int i = 0; i < stones.size(); i++) sum += stones[i];

        int bagweight = sum / 2;
        vector<int> dp(bagweight + 1, 0);
        for(int i = 0; i < stones.size(); i++){
            for(int j = bagweight; j >= stones[i]; j--){
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }

        return (sum - dp[bagweight] - dp[bagweight]);
    }
};

494.目标和

文档讲解:代码随想录 (programmercarl.com)

视频讲解:动态规划之背包问题,装满背包有多少种方法?| LeetCode:494.目标和_哔哩哔哩_bilibili

状态:做不出来。下面写的详解比“文档讲解”和“视频讲解”好。

又一个新类型题:前面的dp[j]表示容量为j的背包能装的物品最大价值是多少,本题的dp[j]表示填满容量为j的背包有几种方法

思路

因为 left组合 - right组合 = target、left + right = sum,target是固定的,sum是固定的,

所以 left = (target + sum) / 2此时问题就转化为,装满容量为(target+sum)/2 的背包,有几种方法

小细节:

  1. 若(target+sum)%2==1,则无解,具体原因参照视频讲解中的举例;
  2. 若abs(target)>sum,也无解。

下面是“视频讲解”下方评论的解法,比up主的清晰,即01背包的过程

首先是二维数组解法

可以把状态定义为dp[i][j],表示用数组中前i个元素组成和为j的方案数。那么状态转移方程就是:

dp【i】【j】 = dp【i-1】[j-nums【i】] + dp【i-1】[j+nums【i】]

这个方程的意思是,如果我们要用前i个元素组成和为j的方案数,那么有两种选择:第i个元素取正号或者取负号。

  • 取正号,那么前i-1个元素的和为j-nums【i】,那么前i-1个元素就要组成和为j-nums【i】的方案数dp【i-1】[j-nums【i】];
  • 取负号,那么前i-1个元素的和为j+nums【i】,那么前i-1个元素就要组成和为j+nums【i】的方案数dp【i-1】[j+nums【i】]。

所以两种选择的方案数相加就是dp【i】【j】。

二维数组解法不需要应用left = (target + sum) / 2公式进行转换。

但是这样定义状态会导致空间复杂度过高,因为我们需要一个二维数组来存储所有可能的状态。

所以应用上面讲的left = (target + sum) / 2公式转换为一维的01背包问题。也就是说,我们只需要找到有多少种方法可以从数组中选出若干个元素使得它们的和等于(target + sum(nums)) / 2即可。这就变成了一个经典的01背包问题。

可以把状态定义为dp【j】,表示用数组中若干个元素组成和为j的方案数。那么状态转移方程就是:

dp【j】 = dp【j】 + dp[j - nums【i】]

这个方程的意思是,如果我们要用若干个元素组成和为j的方案数,那么有两种选择:不选第i个元素或者选第i个元素。

如果不选第i个元素,那么原来的元素和为j,那么原来已经有多少种方案数就不变,即dp【j】;

如果选第i个元素,那么剩下的元素和为j - nums【i】,要组成和为j - nums【i】 的方案数就等于dp[j - nums【i】]。

所以两种选择相加就是等号左边的dp【j】。

但是在实现这个状态转移方程时,有一个细节需要注意:由于每次更新dp【j】都依赖于之前计算过得dp值(也就是说当前行依赖于上一行),所以我们必须从后往前遍历更新dp值(也就是说从右往左更新),否则会覆盖掉之前需要用到得值。

最后返回dp【bag_size】即可。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(int i = 0; i < nums.size(); i++) sum += nums[i];    // 计算数组中所有元素的和
        if((target + sum) % 2 == 1) return 0;   // 无解情况1
        if(abs(target) > sum) return 0;     //无解情况2
        int bagWeight = (target + sum) / 2;     //计算背包容量,也就是要组成的和

        vector<int> dp(bagWeight + 1, 0);   //初始化动态规划数组,长度为背包容量加一,初始值都为零
        dp[0] = 1;      //表示用若干个元素组成和为零的方案数,只有一种就是什么都不选

        for(int i = 0; i < nums.size(); i++){   // 遍历数组中每个元素
            for(int j = bagWeight; j >= nums[i]; j--){  // 倒序遍历背包容量从大到小,直到小于当前元素值停止
                dp[j] = dp[j] + dp[j - nums[i]];    // 更新dp【j】,表示选或者不选当前元素的方案数之和
            }
        }
        return dp[bagWeight];  //和为bagWeight的情况有几种
    }
};

474.一和零

文档讲解:代码随想录 (programmercarl.com)

视频讲解:动态规划之背包问题,装满这个背包最多用多少个物品?| LeetCode:474.一和零_哔哩哔哩_bilibili

状态:不会做。这是“背包的有两个维度”的题目。

思路

本题中strs 数组里的元素就是物品,不同长度的字符串就是不同大小的待装物品,每个物品都是一个!

而m 和 n相当于是一个背包,两个维度的背包

动规五部曲

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

    dp[i][j]:最多有i个0和j个1的strs的最大子集的大小dp[i][j]

  2. 确定递推公式

    dp[i][j] 可以由前一个strs里的元素(字符串)推导出来,假设字符串t有zeroNum个0,oneNum个1。

    dp[i][j] = dp[i - zeroNum][j - oneNum] + 1。 dp[i - zeroNum][j - oneNum] 表示不包含字符串t的最大子集大小。

    在遍历的过程中,取dp[i][j]的最大值。所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);


    此时大家可以回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    对比就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。

    这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。

  3. dp数组如何初始化

    01背包的dp数组初始化为0就可以。因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

  4. 确定遍历顺序

    外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

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

    for (string str : strs) { // 遍历物品
        int oneNum = 0, zeroNum = 0;
        for (char c : str) {
            if (c == '0') zeroNum++;
            else oneNum++;
        }
        for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
            for (int j = n; j >= oneNum; j--) {
                dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
            }
        }
    }
    

​ 遍历背包容量的两层for循环先后循序有没有什么讲究?

​ 没讲究,都是物品重量的一个维度,先遍历哪个都行!

代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0));  // 默认初始化0

        for(string str: strs){  // 遍历物品
            int zeroNum = 0, oneNum = 0;
            for(char c: str){
                if(c == '0') ++zeroNum;
                else ++oneNum;
            }

            for(int i = m; i >= zeroNum; i--){  // 遍历背包容量且从后向前遍历!
                for(int j = n; j >= oneNum; j--){
                    dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

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

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

相关文章

chatgpt如何引入领域知识?mit团队利用gpt4做数据增强来提升小模型在特定领域的效果

一、概述 title&#xff1a;Dr. LLaMA: Improving Small Language Models in Domain-Specific QA via Generative Data Augmentation 论文地址&#xff1a;Paper page - Dr. LLaMA: Improving Small Language Models in Domain-Specific QA via Generative Data Augmentation…

(6)LED点阵屏

LED点阵屏由若干个独立的LED组成&#xff0c;LED以矩阵的形式排列&#xff0c;以灯珠亮灭来显示文字、图片、视频等。LED点阵屏广泛应用于各种公共场合&#xff0c;如汽车报站器、广告屏以及公告牌等 LED点阵屏分类 按颜色&#xff1a;单色、双色、全彩按像素&#xff1a;88、…

Excel模板导入导出功能测试点

近期接触的都是Web项目&#xff0c;有很多导入数据这个功能&#xff0c;导入的文件格式都是Excel&#xff0c;基本流程就是&#xff1a;下载一个Excel模板&#xff0c;填充数据&#xff0c;再将Excel表格导入&#xff0c;导入后可下载列表&#xff0c;想着这类功能的测试点基本…

springboot基于vue的地方美食分享网站

开发技术介绍 Java介绍 JavaScript是一种网络脚本语言&#xff0c;广泛运用于web应用开发&#xff0c;可以用来添加网页的格式动态效果&#xff0c;该语言不用进行预编译就直接运行&#xff0c;可以直接嵌入HTML语言中&#xff0c;写成js语言&#xff0c;便于结构的分离&…

DVWA全级别通关教程

首先选择难度&#xff0c;我们从low开始&#xff0c;如上图所示进行修改 目录 SQL手工注入 过程&#xff1a; low Medium high Impossible SQL 盲注 过程&#xff1a; SQL 工具注入 工具安装过程&#xff1a; 过程&#xff1a; low Medium High: 暴力破解 过…

教室借用-贪婪算法

问题1&#xff1a; 假设你有一个教室&#xff08;只有一个教室&#xff09;&#xff0c;你有几个来自讲师的使用教室的请求 。应用间隔调度问题来安排尽可能多的请求&#xff0c;以优化教室的使用。 最早开始时间 最早开始时间&#xff08;Earliest Start Time&#xff0c;简…

一站了解zookeeper的关键知识

ZooKeeper 是 Apache 软件基金会的一个软件项目&#xff0c;它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&#xff0c;构成一个高…

maven下载和maven配置(以maven3示例)

maven下载和maven配置&#xff08;以maven3示例&#xff09; 1.maven下载1.1 直接使用idea捆绑的&#xff08;很轻松&#xff09;1.2 手动下载到本机服务器&#xff08;推荐&#xff09;1.2.1 官网下载maven1.2.2 选好版本后&#xff0c;选二进制目录 binaries1.2.3 文件选择1.…

Jmeter的Content-Type设置方式

今天调Jmeter脚本遇到一个问题&#xff1a;接口的请求体为Body Data时&#xff0c;没有在HTTP信息头管理加Content-Type参数&#xff0c;Content-Type: application/json&#xff0c;导致脚本一直跑不通&#xff0c;报错&#xff0c;一顿排查&#xff0c;才发现是请求头的原因。…

SpringCloud项目将某个子模块改成以war的形式打包,需要修改的地方

SpringCloud项目将某个子模块改成以war的形式打包&#xff0c;需要修改的地方 在GAV后面添加 《packaging》 war 《/packaging》 在GAV后面添加 《packaging》 war 《/packaging》 添加war打包方式 <packaging>war</packaging>添加之后项目会报错&#xff0c;工程…

Ada学习(2)Statements

文章目录 if statements / expressionif statementsif expression Case Statement / ExpressionCase StatementCase Expression Loop Statement基本循环结构 loopwhile loopFor 循环NoteFor ... loop Control VariableFor ... loop Range EvaluationDeclare block (声明代码块)…

手把手教小白安装Jenkins

一、Jenkins简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 简而言之&#xff0c;Jenkins就是一款将构建&#xff0c;打…

例3:模板

例&#xff1a;如图所示流程&#xff0c;乙烷和丙烷混合液进入蒸发器Evaporator&#xff0c;从天然气吸收热量后进入压缩机Compressor&#xff0c;压缩后的气体进入冷凝器Condenser&#xff0c;最后通过J-T阀&#xff08;焦耳-汤姆逊节流膨胀阀&#xff09;回到进入蒸发器前的状…

抖音SEO矩阵营销系统/MVC源码部署二次开发搭建

首先&#xff0c;抖音SEO矩阵系统源码开发&#xff0c;如何做独立部署&#xff0c;首先我们需要深刻理解这个系统的开发逻辑是什么&#xff1f;开发的前言是在抖音平台做流量新增&#xff0c;现在抖音及各大主流短视频平台&#xff0c;流量新增&#xff0c;各大企业需要在短视频…

10- YOLO算法一 (目标检测)

要点&#xff1a; yolo算法属于 One-stage detector 一 YOLO v1 1) 将一幅图像分成SxS个网格(grid cell)&#xff0c;如果某个object的中心 落在这个网格中&#xff0c;则这个网格就负责预测这个object。 2)每个网格要预测B个bounding box&#xff0c;每个bounding box 除了…

springboot配置多个数据源【详解】

springboot配置多个数据源【详解】 前言&#xff0c;什么是数据源与数据库连接池一、配置文件进行配置&#xff1a;1.导入依赖&#xff1a; 二、编写配置类&#xff1a;1.用来指定包扫描、指定sqlSessionTemplateRef2&#xff0c;用来指定mapper.xml的路径3.Mybatis主数据源ds1…

DNS协议、ICMP协议、NAT技术

目录 一、DNS协议 1.1 认识DNS 1.2 域名简介 1.3 域名解析过程 1.4 使用dig工具分析DNS过程 二、ICMP协议 2.1 ICMP协议的定位 2.2 ICMP功能 2.3 ICMP协议格式&#xff08;了解&#xff09; 2.4 ping命令 2.5 traceroute命令 三、NAT技术&#xff08;重点&#xff…

JavaWeb13-JavaScript 开发利器之 jQuery-01

1. 说明 1.1 现状 1、jquery 使用情况 2、Vue 使用情况 1.2 官方文档 学习地址: https://www.w3school.com.cn/jquery/index.asp API地址: https://jquery.cuishifeng.cn/ 1.3 JQuery 是什么? 1.3.1 基本介绍 jQuery 是一个快速的&#xff0c;简洁的 javaScript 库…

BAPC 2022 Pre 部分题解

BAPC 2022 Pre 部分题解 K (11). Lots of Liquid题目描述题意思路代码 F (6). Fastestest Function题目描述题意思路代码 B (2). Bubble-bubble Sort题目代码 A (1). Abbreviated Aliases题目题意思路代码 I (9). Jabbing Jets题目题意思路代码 E (5). Extended Braille题目题意…

SQL调优-性能参数介绍

-- 课程表 create table course ( cid int(3), cname varchar(20), tid int(3) ); -- 教师表 create table teacher (tid int(3),tname varchar(20),tcid int(3) ); -- 教师证表 create table teacherCard (tcid int(3),tcdesc varchar(200) );explain select语句分析 1.id…