力扣动态规划专题(二)01背包 416. 分割等和子集 1049.最后一块石头的重量II 494. 目标和 474. 一和零 步骤及C++实现

news2024/11/15 10:02:03

文章目录

  • 01背包
    • 二维dp数组
    • 一维dp数组 滚动数组
  • 416. 分割等和子集
  • 1049.最后一块石头的重量II
  • 494. 目标和
  • 474. 一和零

01背包

在这里插入图片描述
完全背包的物品数量是无限的,01背包的物品数量只有一个。

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

重量价值
物品0115
物品1320
物品2430

二维dp数组

  1. 确定dp数组以及下标的含义:二维数组dp[i][j],表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少,如
背包重量j01234
物品0
物品1
物品2
  1. 确定递推公式,有两个方向推出来dp[i][j]:
  • 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]=dp[i - 1][j]。意味着,此时物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。
  • 放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,此时dp[i][j]=dp[i - 1][j - weight[i]] + value[i],物品i的价值为value[i]。意味着,物品i的重量小于背包j的重量时,背包放物品i得到的最大价值。
  • 递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
  1. dp数组如何初始化
    情况1:j=0时,dp[i][0]=0,此时背包容量j为0,无论选取什么物品,背包价值总和为0
    情况2:i=0时,dp[0][j],表示存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
    当 j < weight[0]时,dp[0][j]=0,因为背包容量比编号0的物品重量还小;
    当j >= weight[0]时,dp[0][j]=value[0],因为背包容量放足够放编号0物品。
背包重量j01234
物品0015151515
物品10
物品20
  1. 确定遍历顺序
    先遍历物品,或者先遍历背包都可以

  2. C++实现

void test_2_wei_bag_problem1() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagweight = 4;

    // 二维数组
    vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));

    // 初始化
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[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]);

        }
    }
    /*
    // weight数组的大小 就是物品个数
	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]);
	    }
	}
	*/

    cout << dp[weight.size() - 1][bagweight] << endl;
}

int main() {
    test_2_wei_bag_problem1();
}

一维dp数组 滚动数组

在二维数组的基础上,把上一层结果覆盖在当前层,即dp[i - 1]那一层拷贝到dp[i]上

  1. 确定dp数组以及下标的含义:一维数组dp[j],容量为j的背包,所背的物品价值可以最大为dp[j]
背包重量j01234
物品0
物品1
物品2
  1. 确定递推公式,有两个方向推出来dp[j]:
    dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。

dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])

  • 不放物品i:由dp[j]本身推出,相当于二维dp数组的dp[i - 1][j]。此时物品i的重量大于背包j的重量时,物品i无法放进背包中,背包内的价值不变。
  • 放物品i:由dp[j - weight[i]] + value[i]推出,物品i的价值为value[i],dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。物品i的重量小于背包j的重量时,背包放物品i得到的最大价值。
  • 递归公式: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
  1. dp数组如何初始化
    情况1:j=0时,dp[0]=0,此时背包容量j为0,无论选取什么物品,背包价值总和为0
    情况2:j≠0时,dp[j]会被覆盖更新。
背包重量j01234
物品0015151515
物品10
物品20
  1. 确定遍历顺序
    二维dp遍历的时候,背包容量是从小到大,先遍历物品或者先遍历背包都可以,正序遍历
    一维dp遍历的时候,背包是从大到小,只能先遍历物品再遍历背包容量,倒序遍历背包容量,都是是为了保证物品i只被放入一次

  2. C++实现

void test_1_wei_bag_problem() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagWeight = 4;

    // 初始化
    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;
}

int main() {
    test_1_wei_bag_problem();
}

416. 分割等和子集

在这里插入图片描述
背包问题确定

元素只能使用一次,不可重复放入,01背包
背包的体积为sum / 2
背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
背包如果正好装满,说明找到了总和为 sum / 2 的子集。

步骤

  1. 确定dp数组以及下标的含义
    题目的每一个元素的数值既是重量,也是价值。
    那么dp[j]表示背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
    背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。

  2. 确定递推公式
    物品i的重量是nums[i],其价值也是nums[i],那么递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

  3. dp数组如何初始化
    j=0时,dp[0]=0,j≠0时,dp[j]会被覆盖更新。
    要注意的是,如果题目给的价值都是正整数,那么非0下标都初始化为0;如果给的价值有负数,那么非0下标就要初始化为负无穷。

  4. 确定遍历顺序
    使用一维dp数组,只能先遍历物品,即物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历

  5. 举例推导dp数组
    dp[j]的数值一定是小于等于j的,如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j。用例1,输入[1,5,11,5] 为例,target = (1+5+11+5) / 2 = 11

下标i01234567891011
01111566661011
  1. C++实现
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        
        // 1 <= nums.length <= 200,1 <= nums[i] <= 100
        //dp数组总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
        vector<int> dp(10001, 0);//dp数组初始化

        int sum = 0;
        for(int i=0; i<nums.size(); i++)
        {
            sum += nums[i];
        }
        if(sum % 2 ==1) return false;
        int target = sum / 2;

        //01背包
        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(dp[target] == target) return true;
        return false;
    }
};

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

在这里插入图片描述
步骤

  1. 确定dp数组以及下标的含义
    物品的重量为stones[i],物品的价值也为stones[i]。
    dp[j]表示容量(重量)为j的背包,最多可以背最大重量为dp[j],最多可以装的价值为 dp[j] = 最多可以背的重量为dp[j]

  2. 确定递推公式
    物品i的重量和价值都是stones[i],那么递推公式:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

  3. dp数组如何初始化
    在这里插入图片描述
    target=最大重量的一半,最大重量100*30=3000
    j=0时,dp[0]=0,重量不可能是负数。

  4. 确定遍历顺序
    使用一维dp数组,只能先遍历物品,即物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历

  5. 举例推导dp数组
    dp[j]的数值一定是小于等于j的,如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j。输入[2,4,1,1],target = (2 + 4 + 1 + 1)/2 = 4

  6. C++实现

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        vector<int> dp(1501, 0);
        int sum = 0;
        for(int i=0; i<stones.size(); i++) sum += stones[i];
        int target = sum / 2;
        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];
    }
};

494. 目标和

在这里插入图片描述
加法总和 + 减法总和 = sum,加法总和为x,那么 x - (sum - x) = target,left = (target + sum)/2 。

步骤

  1. 确定dp数组以及下标的含义
    一维dp数组,dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
    二维dp数组,dp[i][j]表示:使用 下标为[0, i]的nums[i]能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法。

  2. 确定递推公式
    nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。
    求组合类问题的公式,都类似:dp[j] += dp[j - nums[i]];

  3. dp数组如何初始化
    初始化 dp[0] 为 1
    情况剔除:如果target > sum,无解;如果(target+sum) % 2 =1,无解

  4. 确定遍历顺序
    使用一维dp数组,只能先遍历物品,即物品遍历在外循环,遍历背包在内循环,且内循环倒序遍历,即nums在外循环,target在内循环,且内循环倒序。

  5. 举例推导dp数组
    nums: [1, 1, 1, 1, 1], target: 3
    bagSize = (target + sum) / 2 = (3 + 5) / 2 = 4

  6. C++实现

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(abs(target) > sum) return 0;
        if((target + sum) % 2 ==1) return 0;
        int bagsize = (target + sum) / 2;
        
        //dp数组 初始化
        vector<int> dp(bagsize + 1, 0);
        dp[0] = 1;

        //dp数组更新 先物品后背包 背包倒序
        for(int i = 0; i<nums.size(); i++)
        {
            for(int j=bagsize; j>=nums[i]; j--)
            {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagsize];
    }
};

474. 一和零

在这里插入图片描述
strs 数组里的元素就是物品,每个物品都是一个;m 和 n相当于是一个背包,两个维度的背包。

步骤

  1. 确定dp数组以及下标的含义
    二维dp数组,dp[i][j]:最多有i个0和j个1的strs的最大子集的大小

  2. 确定递推公式

  • 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背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);,字符串的zeronum和onenum相当于物品的重量weight[i],字符串本身的个数相当于物品的价值value[i]
  1. dp数组如何初始化
    初始化 dp[0] 为0
    物品价值不会是负数,dp数组初始为0,保证递推的时候dp[i][j]不会被初始值覆盖

  2. 确定遍历顺序
    使用一维dp数组,只能先遍历物品,即物品遍历在外循环,遍历背包在内循环,且内循环倒序遍历,即strs里的字符串在外循环,m和n在内循环,且内循环倒序。

  3. 举例推导dp数组
    以输入:[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例,

  4. C++实现
    注意字符串的遍历

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        //dp数组 默认初始化为0
        vector<vector<int>> dp(m+1, vector<int>(n+1, 0));
        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);
                }
            }
        }
        return dp[m][n];
    }
};

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

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

相关文章

基于ArcGIS的nc(NETCDF)转tif格式

软件版本&#xff1a;ArcMap10.4.1 nc(NETCDF)是一组独立于机器的软件库支持创建、访问和共享面向阵列的数据格式科学数据&#xff0c;它也是共享科学数据的社区标准。&#xff08;摘自Unidata官网&#xff09;&#xff0c;被广泛应用于大气、海洋、水文等领域&#xff0c;是我…

stm32读取BH1750光照传感器

stm32读取BH1750光照传感器 一.序言二.BH1750指令三.IIC协议四.代码实例4.1 bh1750.c源文件4.2 bh1750.h头文件 一.序言 BH1750是用IIC协议进行数据传输的。有SCL,SDA&#xff0c;VCC,GND四根线。下图是原理图 二.BH1750指令 我们先看芯片手册的操作指令&#xff08;下图&a…

直播 RTM 推流在抖音的应用与优化

动手点关注 干货不迷路 背景 随着互联网技术以及网络基建的快速发展和普及&#xff0c;视频直播已经成为了一种越来越普遍的娱乐和社交方式。无论是个人还是企业&#xff0c;都可以通过视频直播平台进行直播活动&#xff0c;向观众展示自己的生活、工作或者产品。同时&#xff…

有什么办法恢复格式后的u盘数据?5个方法,赶紧收藏起来

随着科技的不断进步&#xff0c;U盘已经成为了我们重要的移动存储设备之一&#xff0c;但是在使用过程中&#xff0c;很多人都可能会不小心将U盘格式化导致数据丢失。那么有什么办法恢复格式后的U盘数据&#xff1f;本文将会为您介绍恢复U盘格式化后数据的5种方法&#xff0c;如…

MT6761/MT6762/MT6765核心板模块 demo串口调试

串口调试 如果正在进行lk(little kernel ) 或内核开发&#xff0c;USB 串口适配器&#xff08; USB 转串口 TTL 适配器的简称&#xff09;对于检查系统启动日志非常有用&#xff0c;特别是在没有图形桌面显示的情况下。 1. 选购适配器 常用的许多 USB 转串口的适配器&#x…

SpringCloud:分布式事务Seata实践优化

1.极致性能优化 1.1. 同库模式 通常&#xff0c;一个TM会产生一笔主事务日志&#xff0c;一个RM会产生一条分支事务日志&#xff0c;每个分布式事务由一个TM和若干 RM组成&#xff0c;一个分布式事务总共会有1N条事务日志&#xff08;N为RM个数&#xff09;。 在默认情况下&…

万物的算法日记|第五天

笔者自述&#xff1a; 一直有一个声音也一直能听到身边的大佬经常说&#xff0c;要把算法学习搞好&#xff0c;一定要重视平时的算法学习&#xff0c;虽然每天也在学算法&#xff0c;但是感觉自己一直在假装努力表面功夫骗了自己&#xff0c;没有规划好自己的算法学习和总结&am…

CTFshow-pwn入门-前置基础pwn5 - pwn12

pwn5-pwn12的题目全是关于汇编语言的知识&#xff0c;pwn5-pwn12的汇编文件的代码都是一样的。 我们将可执行文件和汇编文件托到ctfshow-pwn专用虚拟机里&#xff0c;给可执行文件加上执行权限并查看其信息。 32位的&#xff0c;直接扔到ida中去。 在虚拟机中使用cat命令读取下…

SpringBoot的配置文件

SpringBoot的配置文件 &#x1f50e;配置文件的作用&#x1f50e;配置文件的格式&#x1f50e;properties配置文件properties的基本语法读取配置文件 &#x1f50e;yml配置文件yml的基本语法读取配置文件Tips关于 \n&#x1f36d;配置对象&#x1f36d;配置集合&#x1f36d; &…

网络知识点之-DNS协议

域名系统&#xff08;Domain Name System&#xff0c;缩写&#xff1a;DNS&#xff09;是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS使用TCP和UDP端口53。当前&#xff0c;对于每一级域名长度的限制是63个…

4.13 ReentrantLock

相对于 synchronized 它具备如下特点 可中断可以设置超时时间可以设置为公平锁支持多个条件变量 与 synchronized 一样&#xff0c;都支持可重入 基本语法// 获取锁reentrantLock.lock();try{// 临界区} finally{// 释放锁reentrantLock.unlock();}1、可重入 可重入是指同一个…

小程序 抽象节点 selectable 与slot区别

比较 了解了微信小程序的抽象节点组件封装方式之后&#xff0c;觉得与vue的slot使用类似&#xff0c;但也有些区别 &#xff1a; 抽象节点 和 slot 有什么不同&#xff1a; slot只需要你传入一段代码抽象节点需要你传入一个自定义组件&#xff0c;&#xff0c;不是让你只传递…

Kubernetes集群本地连接调试工具KtConnect

一、简介 KtConnect&#xff08;Kt为Kubernetes Toolkit集群工具包的简写&#xff09;是一款基于Kubernetes环境用于提高本地测试联调效率的小工具 Connect&#xff1a;建立数据代理通道&#xff0c;实现本地服务直接访问Kubernetes集群内网&#xff08;包括Pod IP和Service域…

Jetpack Compose教程-水位控制小部件

Jetpack Compose教程-水位控制小部件 Apple的应用程序和小部件一直是设计的典范&#xff0c;也给我们的"复制系列&#xff1a;活动应用"和"卡片应用"提供了灵感。当他们发布了新款苹果手表Ultra时&#xff0c;它里面深度测量小部件的设计引起了我们的兴趣&…

加快奔向“国际数字之都” CDEC2023中国数字智能生态大会走进上海

数智闪耀长三角&#xff0c;风云际会上海滩。 6月14日上午&#xff0c;以汇聚数字产业动能、打造区域合作为主旨的 CDEC2023中国数字智能生态大会上海站活动在浦东软件园创新体验中心举行。 大会以“共建AI智能生态”为主题&#xff0c;吸引致远互联、SAP、浪潮等龙头企业&…

2022年山东省职业院校技能大赛网络搭建与应用赛项网络搭建与安全部署服务器配置及应用

2022年山东省职业院校技能大赛 网络搭建与应用赛项 第二部分 网络搭建与安全部署&服务器配置及应用 竞赛说明&#xff1a; 一、竞赛内容分布 竞赛共分二个模块&#xff0c;其中&#xff1a; 第一模块&#xff1a;网络搭建及安全部署项目 第二模块&#xff1a;服务器…

C#里的var和dynamic区别到底是什么,你真的搞懂了嘛

前言 这个var和dynamic都是不确定的初始化类型&#xff0c;但是这两个本质上的不同。不同在哪儿呢?var编译阶段确定类型&#xff0c;dynamic运行时阶段确定类型。这种说法对不对呢&#xff1f;本篇看下,文章原文地址&#xff1a;在这里 概括 以下详细叙述下这两个(var,dynamic…

CVE-2023-33246命令执行复现分析

简介 RocketMQ是一款低延迟、高并发、高可用、高可靠的分布式消息中间件。既可为分布式应用系统提供异步解耦和削峰填谷的能力&#xff0c;同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。 影响版本 <RocketMQ 5.1.0 <RocketMQ 4.9.5 环境搭建 docker…

Leetcode 剑指 Offer II 031. 最近最少使用缓存

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 运用所掌握的数据结构&#xff0c;设计和实现一个 LRU (Least Re…

Python 类型检测:isinstance() 与 type()

文章目录 参考描述面向对象编程概念类与实例继承super() 与代理对象方法的自动继承属性的继承 isinstance 与 type 内置函数isinstance()可迭代对象仅能为元组可能产生的 TypeError嵌套的元组 typeisinstance() 与 type() 的区别 参考 项目描述Python 官方文档https://docs.py…