动态规划--01背包问题详解

news2024/11/30 2:31:35

代码随想录day42和day43 动态规划 模块01背包问题
“即使到不了远方,心中也要有远方的模样。”

文章目录

    • 1. 01背包理论基础
        • 1.1什么是背包问题
        • 1.2二维dp数组01背包
        • 1.3一维dp数组(滚动数组)01背包
    • 2.leetcode 416.分割等和子集
        • 2.1 详细思路及思考难点
        • 2.2具体步骤及代码实现
    • 3.leetcode 1049.最后一块石头的重量
        • 3.1详细思路及思考难点
        • 3.2具体步骤及代码实现
    • 4.leetcode 494.目标和
        • 4.1详细思路及思考难点
        • 4.2具体步骤及代码实现
    • 5.leetcode 474.一和零
        • 5.1详细思路及思考难点
        • 5.2具体步骤及代码实现

1. 01背包理论基础

1.1什么是背包问题

在这里插入图片描述
  背包问题大致可以分为以上几类,背包问题也就是选取物品放入重量为n的背包中,然后求背包的最大价值。

1.2二维dp数组01背包

  01背包问题—有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
在这里插入图片描述
例子:假设背包最大重量为4,然后有三个物品,看如何将物品放入背包中能获取最大价值。
在这里插入图片描述

还是可以用动态规划的做题步骤来分析

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

dp[i][j]对应的就是取索引[0,i]的物品,放入重量为j的背包中,得到价值dp[i][j]

   2. 确定递推公式

可以先将每种情况列举出来,然后再推导公式
在这里插入图片描述
由此可以推导出递推公式为dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
    每次遍历到dp[i][j]的时候都有两种取值情况dp[i-1][j]表示不放物品时的情况  
   dp[i-1][j-weight[i]]+value[i],weight[i]表示i的重量,value[i]表示i的价值。这就是表示添加新物品的情况
在上面两种情况中取最值

  3. dp数组的初始化问题

当背包重量为0时,可以初始化dp[i][0]=0;
在这里插入图片描述

  4.确定遍历顺序

这题的从前往后面遍历,可以先遍历背包再遍历物品也可以先便利物品再遍历背包,一般是先遍历物品

  5. 举例推导dp公式
在这里插入图片描述
代码示例

    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagsize = 4;
        testweightbagproblem(weight, value, bagsize);
    }

    public static void testweightbagproblem(int[] weight, int[] value, int bagsize){
        int wlen = weight.length, value0 = 0;
        //定义dp数组:dp[i][j]表示背包容量为j时,前i个物品能获得的最大价值
        int[][] dp = new int[wlen + 1][bagsize + 1];
        //初始化:背包容量为0时,能获得的价值都为0
        for (int i = 0; i <= wlen; i++){
            dp[i][0] = value0;
        }
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 1; i <= wlen; i++){
            for (int j = 1; j <= bagsize; j++){
                if (j < weight[i - 1]){
                    dp[i][j] = dp[i - 1][j];
                }else{
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);
                }
            }
        }
        //打印dp数组
        for (int i = 0; i <= wlen; i++){
            for (int j = 0; j <= bagsize; j++){
                System.out.print(dp[i][j] + " ");
            }
            System.out.print("\n");
        }
    }

1.3一维dp数组(滚动数组)01背包

  上面用二维数组来解决01背包问题,其实也可以用一维数组(也叫做滚动数组)来解决01背包问题。其核心也就是将二维数组压缩成一维数组。
假设背包最大重量为4,然后有三个物品,看如何将物品放入背包中能获取最大价值。
在这里插入图片描述

  那还是按照动态规划的做题步骤来分析

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

dp[j]对应的就是背包重量为j的时候对应的最大价值dp[j]

   2. 确定递推公式

可以先将每种情况列举出来,然后再推导公式
在这里插入图片描述

dp[j]是由dp[j-weight[i]]决定的,递推公式可以如下
dp[j]=Math.max(dp[j],dp[j-weight[i]]+value[i])

  3. dp数组的初始化问题

当背包重量为0时,可以初始化dp[0]=0;
在这里插入图片描述

  4.确定遍历顺序

这题先遍历物品的时候可以从前面往后面遍历,然后再背包的时候得从后面往前面进行倒序遍历。如果是顺序遍历的话物品就会重复添加,因为每个物品只能用一次,以免影响其他结果

  5. 举例推导dp公式
在这里插入图片描述
代码示例

    public static void main(String[] args) {
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        int bagWight = 4;
        testWeightBagProblem(weight, value, bagWight);
    }

    public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagWeight + 1];
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagWeight; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        //打印dp数组
        for (int j = 0; j <= bagWeight; j++){
            System.out.print(dp[j] + " ");
        }
    }

2.leetcode 416.分割等和子集

力扣题目链接
在这里插入图片描述

2.1 详细思路及思考难点

  这题可以将其划分为01背包问题,给定一个数组,判断是否能将数组分成两个子集和相同的两个数组。要将数组分成两个相等的子集的话,数组中的和sum也就是必须是偶数(这是第一个判断条件),然后令target=sum/2;这就是子集的和,把target想象成一个背包的最大重量,现在就是让背包里面的物品的重量刚好能达到背包的最大重量,如果能达到就返回true,反之,return false。

2.2具体步骤及代码实现

  按照动态规划的做题步骤来分析

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

dp[j]对应的就是背包重量为j的时候对应的最大价值dp[j]

   2. 确定递推公式

dp[j]是由dp[j-weight[i]]决定的,递推公式可以如下
dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]),这里nums[i]既可以表示重量也可以表示价值

  3. dp数组的初始化问题

当背包重量为0时,可以初始化dp[0]=0;

  4.确定遍历顺序

这题先遍历物品的时候可以从前面往后面遍历,然后再背包的时候得从后面往前面进行倒序遍历。因为每个物品只能用一次,以免影响其他结果

  5. 举例推导dp公式
在这里插入图片描述
代码实现

class Solution {
    public boolean canPartition(int[] nums) {
     if(nums==null || nums.length==0)return false;
     int sum=0;
     for(int a:nums){
        sum+=a;
     }
     if(sum%2!=0)return false;
     int targrt=sum/2;
     int[] dp=new int[targrt+1];
     //dp[0]=0;
     for(int i=0;i<nums.length;i++){
         for(int j=targrt;j>=nums[i];j--){
            dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
         }
     }
     return dp[targrt]==targrt;
    }
}

3.leetcode 1049.最后一块石头的重量

力扣题目链接
在这里插入图片描述

3.1详细思路及思考难点

  这题思路跟416.分割子集问题基本一致,可以将所有石头分成两份和相同的子集和,target=sum/2,然后将石头存入dp[target]中所能满足的最大价值,最终返回的结果是(sum-dp[target])-dp[target],(sum-dp[target])表示没有装入背包的所有的集合,然后减去装入背包的,就是碰撞之后剩下的量。

3.2具体步骤及代码实现

  按照动态规划的做题步骤来分析

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

dp[j]对应的就是背包重量为j的时候对应的最大石头量dp[j]

   2. 确定递推公式

dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

  3. dp数组的初始化问题

当背包重量为0时,可以初始化dp[0]=0;

  4.确定遍历顺序

这题先遍历物品的时候可以从前面往后面遍历,然后再背包的时候得从后面往前面进行倒序遍历。因为每个物品只能用一次,以免影响其他结果

  5. 举例推导dp公式
在这里插入图片描述
代码实现

class Solution {
    public int lastStoneWeightII(int[] stones) {
    int sum=0;
    for(int a:stones){
     sum+=a;
    }
    int target=sum>>1;
     int[] dp=new int[target+1];
     dp[0]=0;
     for(int i=0;i<stones.length;i++){
         for(int j=target;j>=stones[i];j--){
             dp[j]=Math.max(dp[j],dp[j-stones[i]]+stones[i]);
         }
     }
     return sum-dp[target]-dp[target];
    }
}

4.leetcode 494.目标和

力扣题目链接
在这里插入图片描述

4.1详细思路及思考难点

  将nums中的数值添加正负号之后,设正数和为x,那么负数的和就是sum-x(sum是nums数组中所有元素的和)。x-(sum-x)=target ==>x=(sum+target)/2。然后将x转换成填满大小为x的背包,需要dp[j]种方法。

4.2具体步骤及代码实现

  按照动态规划的做题步骤来分析

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

dp[j]对应的就是背包重量为j的时候对应的最大价值dp[j]

   2. 确定递推公式

根据下面得出递推公式为dp[j]+=dp[j-nums[i]]

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 dp[5]。
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 dp[5]。
  • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 dp[5]
  • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 dp[5]
  • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 dp[5]

  3. dp数组的初始化问题

当背包重量为0时,可以初始化dp[0]=0;

  4.确定遍历顺序

这题先遍历物品的时候可以从前面往后面遍历,然后再背包的时候得从后面往前面进行倒序遍历。因为每个物品只能用一次,以免影响其他结果

代码实现

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
     int sum=0;
     for(int a:nums){
        sum+=a;
     }
     int zs=(sum+target)/2;
      if((target+sum)%2!=0) return 0;
     if(zs<0)zs=-zs;
     int[] dp=new int[zs+1];
     if(target>sum)return 0;
     dp[0]=1;
     for(int i=0;i<nums.length;i++){
         for(int j=zs;j>=nums[i];j--){
             dp[j]+=dp[j-nums[i]];
         }
     }
     return dp[zs];
    }
}

5.leetcode 474.一和零

力扣题目链接
在这里插入图片描述

5.1详细思路及思考难点

  这题用二维dp数组来做,把每个字符串当作单独的物品,总共的0和1的个数当作背包的容量。

5.2具体步骤及代码实现

  按照动态规划的做题步骤来分析

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

dp[i][j]表示当有i个0和j个1时的最大子集的大小

   2. 确定递推公式

根据下面得出递推公式为dp[i][j]=Math.max(dp[i][j],dp[i-zero][j-one]+1)

  3. dp数组的初始化问题

初始为0就可以

  4.确定遍历顺序

遍历字符串的时候顺序遍历,遍历0和1的时候倒叙

  5.推导dp数组
在这里插入图片描述
代码实现

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
       int[][] dp=new int[m+1][n+1];
       int zero,one;
       for(String s : strs){
           zero=0;
           one=0;
           for(char c:s.toCharArray()){
              if(c=='0'){
                  zero++;
              }else if(c== '1'){
                  one++;
              }
           }
           for(int i=m;i>=zero;i--){
               for(int j=n;j>=one;j--){
                   dp[i][j]=Math.max(dp[i][j],dp[i-zero][j-one]+1);
               }
           }
       }
       return dp[m][n];
    }
}

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

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

相关文章

当食品制造业遇见数字化工具,如何借助S2B2C电商系统实现企业新增长

食品制造业是我国产业发展中的重要组成部分&#xff0c;具有点多、面广、投资小、见效快的特点&#xff0c;在经济发展中发挥着重要作用。根据工信部数据统计&#xff0c;从2018至2021年我国食品制造业经营规模稳步增长&#xff0c;2021年我国食品制造业营业收入达21268.1亿元&…

QT·移植Qt到ARM平台及搭建Qt交叉编译环境

目录 一、编译tslib库 二、移植 tslib 到文件系统 三、编译Qt源码&#xff08;用于移植到ARM&#xff09; 四、移植Qt到文件系统 五、搭建 Qt Creator 交叉编译环境 六、获得Qt可执行文件的另一种方法 要想在ARM平台上运行Qt程序&#xff0c;得满足以下两个点&#xff1a;1、…

【表达式求值】

目录&#xff1a;前言一、有效的括号&#xff08;一&#xff09;题目分析&#xff08;二&#xff09;整体代码二、表达式求值&#xff08;一&#xff09;题目分析1.栈的基本操作&#xff1a;2. 大体思路&#xff1a;3.具体计算过程&#xff1a;&#xff08;二&#xff09;整体代…

【代码随想录】二刷-哈希表

哈希表 《代码随想录》 哈希表一般用来快速查找某个元素是否在一个集合中。如果使用枚举的话时间复杂度为O(n)&#xff0c;而使用哈希表只O(1)就可以做到。——元素查询。 242.有效的字母异位词 使用unordered_map // 时间复杂度 O(n) // 空间复杂度 O(n) class Solution { pub…

嵌入式分享合集94

一、单片机硬件电路设计 减少后级电源对前级的影响&#xff0c;防止电源正负接反烧坏后级电路&#xff0c;防止电源关电时电流倒灌&#xff0c;但经过二极管有0.4V左右压降&#xff0c;需要考虑经过0.4V降压后会不会低于后级电路的正常工作电压。 一、按键电路 R1上拉电阻&…

用C语言开发入门游戏FlappyBird

前言 《flappy bird》是一款由来自越南的独立游戏开发者Dong Nguyen所开发的作品&#xff0c;游戏于2013年5月24日上线&#xff0c;并在2014年2月突然暴红。2014年2月&#xff0c;《Flappy Bird》被开发者本人从苹果及谷歌应用商店撤下。2014年8月份正式回归APP Store&#xf…

java从零开始系统性学习完整笔记(一)

java从零开始系统性学习完整超全资源笔记(还在更新中&#xff09; 前言 资源来自&#xff1a;王汉远java基础&#xff0c; B站视频&#xff1a; https://www.bilibili.com/video/BV1b4411g7bj/?spm_id_from333.1007.top_right_bar_window_custom_collection.content.click&a…

渗透测试之分享常用工具、插件和脚本(干货)

BRUP插件&#xff1a; 漏洞挖掘插件&#xff1a;Autorize、CSRF Token Tracker、XSS Validator、Turbo Intruder 辅助插件&#xff1a;HaE、sqlmap4brup、hackbar、Software Vulnerability Scanner 浏览器插件&#xff1a; wappalyzer、MySSL、Cookie Editor 脚本&#xff1a; …

Vue学习:vue生命周期

Vue实例有一个完整的生命周期&#xff0c;也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程&#xff0c;我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程&#xff0c;就是生命周期。生命周期又称为生命周期回调函数&#…

水果叠叠乐

水果叠叠乐 介绍 消消乐是一款益智类小游戏&#xff0c;最近比较火爆的一种是立体叠叠乐式的&#xff0c;然后小蓝也想开发一个自己练练手&#xff0c;给它起名叫“水果叠叠乐”。 准备 本题已经内置了初始代码&#xff0c;打开实验环境&#xff0c;目录结构如下&#xff1…

OSPF高级配置——学习OSPF路由协议的高级应用

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.路由重分发及配置 1.路由重分发概述 2.理解路由重分发 3.路…

【HMS Core】华为分析SDK如何申请数据导出功能?

1、问题描述 项目中集成了华为分析SDK&#xff0c;现在有一个需求&#xff0c;想要申请数据导出功能&#xff0c;申请页面提示数据导出功能目前仅对部分邀请伙伴开放&#xff0c;需要通过在线提单的方式申请开通此功能&#xff0c;那么具体该如何操作呢&#xff1f; 2、解决方…

校园跑腿微信小程序,校园跑腿小程序,微信小程序跑腿系统毕设作品

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于微信校园跑腿小程序系统&#xff0c;前台用户使用小程序发布跑腿任何和接跑腿任务&#xff0c;后台管理使用基于PHPMySql的B/S架构&#xff1b;通过后台管理跑腿的用户、查看跑腿信息和对应订单。 意义…

C++ Reference: Standard C++ Library reference: C Library: cwchar: wcstoll

C官网参考链接&#xff1a;https://cplusplus.com/reference/cwchar/wcstoll/ 函数 <cwchar> wcstoll long long int strtoll (const wchar_t* str, wchar_t** endptr, int base); 将宽字符串转换为long long整数 解析C宽字符串str&#xff0c;将其内容解释为指定base的…

高级FPGA设计结构实现和优化_(六)静态时序分析

高级FPGA设计结构实现和优化_静态时序分析标准分析锁存器组合反馈标准分析 静态时序分析(STA)指的是在一个设计中与一组约束有关的所有时序路径的综合性分析&#xff0c;为了确定一个设计是否是“时序一致的”。由FPGA设计者遇到的基本路径是输入到触发器、触发器到触发器、触发…

MySQL进阶一 一条select语句的执行流程

文章目录前言MySQL的执行流程第一步&#xff1a;连接器第二步&#xff1a;查询缓存第三步&#xff1a;解析SQL第四步&#xff1a;执行 SQL预处理器优化器执行器主键索引查询全表扫描索引下推总结前言 有一位同志问我为什么很久没更新了&#xff0c;因为前一个礼拜在复盘JavaSE…

【详解】手撕 一维、二维、三维差分数组原理(附图解,模板,例题分析)

【差分专题】 引言 ​ 差分是一种处理数据巧妙而简单的方法&#xff0c;可以应用于区间修改和询问问题。例如&#xff0c;将给定的数据集合 A 分成很多区间&#xff0c;并对这些区间进行很多次操作&#xff0c;每次都是对某段区间内的所有元素做相同的加减操作&#xff0c;此…

YOLOv7学习笔记(一)——概述+环境+训练

一、环境安装测试 1、创建环境conda create -n yolo python3.7conda activate yolo2、安装pytorchconda install pytorch torchvision cudatoolkit11.3 -c pytorch11.3为cuda版本号3、克隆yolov5git clone https://github.com/WongKinYiu/yolov7 # clonecd yolov7pip install…

真的要转到云IDE了吗?VS Code的开源IDE

云IDE产品介绍 云IDE使用教程 免费使用地址&#xff1a;点击【云IDE】&#xff0c;即可开始创建工作空间啦~ 前言 CSDN最新产品【云IDE】来啦&#xff01;【云IDE】将为各位技术er一键秒级构建云开发环境&#xff0c;提升开发效率&#xff01; 1. 什么是IDE&#xff1f; 做…

PyTorch 1.13 正式发布:CUDA 升级、集成多个库、M1 芯片支持

内容导读&#xff1a;近日&#xff0c;PyTorch 团队在官方博客宣布 Pytorch 1.13 发布。本文将详细围绕新版本的 4 大亮点展开介绍。 据官方介绍&#xff0c;PyTorch 1.13 中包括了 BetterTransformer 的稳定版&#xff0c;且不再支持 CUDA 10.2 及 11.3&#xff0c;并完成了向…