背包问题——“0-1背包”,“完全背包”(这样讲,还能不会?)

news2024/11/15 23:30:04

目录

一、0-1背包

1.1、0-1背包解决的问题

1.2、dp数组定义

1.3、转移方程

1.3.1、二维dp数组

1.3.2、一维dp数组

1.4、遍历顺序

1.5、测试代码

1.6、练习

二、完全背包

2.1、完全背包解决问题

2.2、与0-1背包的区别

2.3、测试代码

2.4、拓展问题:装满背包有几种方法?

2.5、排列与组合

2.5.1、组合

2.5.2、排列

2.6、练习


一、0-1背包

1.1、0-1背包解决的问题

        给你 i 个物品,每个物品都具有两个属性(价值value[ i ]和重量weight[ i ]),将他们放入容量为 j 的背包中(不可以重复放入同一个物品),怎么放才能让背包的价值最大?

1.2、dp数组定义

一维和二维都可以这样定义:

dp[ i ][ j ] 或 dp[ j ]:从前i个物品中任选装满容量为j的背包的最大价值

1.3、转移方程

转移方程:

二维:dp[ i ][ j ] =  Math.max( dp[ i - 1 ][ j ], dp[ i - 1 ][ j - weight[ i ] ] +  value[ i ] ) ;

一维(状态压缩,优化):dp[ j ] = Math.max( dp[ j ], dp[ j - weight[ i ] + value[ i ]] );

怎么得出来的???

1.3.1、二维dp数组

注意:如果看不懂了,多往上看看dp数组的定义~

        dp[ i ][ j ]这个状态,就是由上一个状态得来,怎么得来?一种是不放第 i 个物品( dp[ i - 1 ][ j ] ),一种是放第 i 个物品 (dp[ i - 1 ][ j - weight[ i ] ] +  value[ i ]);

不放第 i 个物品的理解:

不放第 i 个物品好理解,无非就是上一个状态,物品还是i - 1个物品,容量还是 j

放第 i 个物品的理解:

可以理解为这么一个过程:(按顺序往下走)

1、[ i - 1 ],这个时候,还没有放入第i个物品,所以这个时候背包里只有装物品 i 之前的所有物品

2、[ j - weight[ i ] ],因为要放物品i,所以要在背包中,给物品 i 预留一个空间,减掉 weight[ i ] 就表示已经给物品 i 腾出空间了,那么接下来就要放入了;

3、+ value[ i ],就表示放入第 i 个物品,那么这个背包里面的价值要增加第 i 个物品的价值;

1.3.2、一维dp数组

        仔细观察,通过对比,你就会发现,二维和一维的dp数组只是少了 i 这个维度,转移方程的形式是没有变的;

为什么能这样压缩成一维dp数组?

        因为一维dp数组实际上对二维矩阵每一层的一个拷贝,例如,遍历到二维矩阵的第三层时,一维dp数组就直接将第二层的数据拷贝过来直接用,以此类推;

值得注意是:压缩成一维以后,遍历背包大小时,要逆序遍历背包的大小(从大到小遍历),原因如下图:

1.4、遍历顺序

对于二维dp数组(未压缩)而言:

先遍历物品,再遍历背包 

或者

先遍历背包,再遍历物品

都可以!

并且

初始化好之后,顺序和逆序遍历背包大小都可以!

对于一维dp数组(压缩)而言:

先遍历物品,再遍历背包 

或者

先遍历背包,再遍历物品

都可以!

但是

初始化好之后,只能逆序遍历背包,原因如下:

1.5、测试代码

二维dp:


    //0-1背包 二维测试代码
    /**
     * @param weight 物品重量
     * @param value 物品价值
     * @param maxWeight 背包最大容量
     * @return
     */
    public int dynamic1(int[] weight, int[] value, int maxWeight) {
        int len = weight.length;//物品数量
        int[][] dp = new int[len][maxWeight + 1];
        //初始化
        for(int i = 0; i <= maxWeight; i++) {
            if(i >= weight[0]) {
                dp[0][i] = value[0];
            }
        }
        for(int i = 1; i < len; i++) { //物品
            for(int j = 0; j <= maxWeight; j++) { //容量
                if(j < weight[i]) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }
        return dp[len - 1][maxWeight];
    }


    public static void main(String[] args) {
        Test3 test3 = new Test3();
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        test3.dynamic1(weight, value, 4);
    }

一维dp:


    /**
     * 0-1背包 一维测试
     * @param weight 物品重量
     * @param value 物品价值
     * @param maxWeight 背包最大容量
     * @return
     */
    public int dynamic2(int[] weight, int[] value, int maxWeight) {
        int len = weight.length;//物品数量
        int[] dp = new int[maxWeight + 1];
        //初始化
        for(int i = 0; i < len; i++) { //物品
            for(int j = maxWeight; j >= weight[i]; j--) {
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[maxWeight];
    }


    public static void main(String[] args) {
        Test3 test3 = new Test3();
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        test3.dynamic2(weight, value, 4);
    }

1.6、练习

建议三刷:

416. 分割等和子集

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

494. 目标和

474. 一和零


二、完全背包

2.1、完全背包解决问题

        给你 i 个物品,每个物品都具有两个属性(价值value[ i ]和重量weight[ i ]),将他们放入容量为 j 的背包中(可以重复放入同一个物品),怎么放才能让背包的价值最大?

2.2、与0-1背包的区别

1. 在转移方程上,几乎没有差别;

2. 在定义dp数组上,一个是可以重复取物品,一个不可以重复取物品;

3. 对于问题“装满背包,有几种方法”这个问题上,遍历顺序上(先遍历背包还是物品?),很有考究,后文有详细讲到

4. 背包容量的遍历顺序上,无论是二维还是一维,都和0-1背包是相反的;

        总的来讲,0-1背包装物品,是不可以重复装同一个物品,而完全背包,是可以重复装一个物品;

        Ps:在完全背包的问题中,建议直接使用一维的dp数组,好理解,也省去了很多不必要的步骤

2.3、测试代码

二维dp:


    /**
     * 完全背包 二维测试代码
     * @param weight 物品重量
     * @param value 物品价值
     * @param maxWeight 背包最大容量
     * @return
     */
    public int dynamic(int[] weight, int[] value, int maxWeight) {
        int len = weight.length;//物品数量
        int[][] dp = new int[len][maxWeight + 1];
        //初始化
        for(int i = weight[0]; i <= maxWeight; i++) {
            dp[0][i] = dp[0][i - 1] + value[0];
        }
        for(int i = 1; i < len; i++) { //物品
            for(int j = maxWeight; j >= 0; j--) {
                if(j < weight[i]) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }
        return dp[len - 1][maxWeight];
    }

    public static void main(String[] args) {
        Test3 test3 = new Test3();
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        test3.dynamic2(weight, value, 4);
    }

一维dp:


    /**
     * 完全背包 一维测试代码
     * @param weight 物品重量
     * @param value 物品价值
     * @param maxWeight 背包最大容量
     * @return
     */
    public int dynamic4(int[] weight, int[] value, int maxWeight) {
        int len = weight.length;//物品数量
        int[] dp = new int[maxWeight + 1];
        //初始化
        for(int i = 0; i < len; i++) { //物品
            for(int j = weight[i]; j <= maxWeight; j++) { //背包
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
        return dp[maxWeight];
    }

    public static void main(String[] args) {
        Test3 test3 = new Test3();
        int[] weight = {1, 3, 4};
        int[] value = {15, 20, 30};
        test3.dynamic4(weight, value, 4);
    }

2.4、拓展问题:装满背包有几种方法?

这是一个拓展的问题,问题就是:装满背包有几种方法?

对于这类问题通解:

dp定义:从前 i 个商品中任取,装满容量为 j 的背包,有dp[ i ]种方法;

dp方程:dp[ j ] = dp[ j ] + dp[ j - weight[ i ] ];

怎么理解?

        不用想太复杂,当前背包装满的方法,就两种情况,一种是装第 i 个物品,一种是不装第 i 个物品;

        求当前背包装满的方法数,就是装与不装第 i 个商品方法的总和;(这里的不装即使指,不装第 i 个物品,通过装满之前背包的方法(物品)来装满背包,所以就是dp[ j ],用的方法一样,所以不变);

2.5、排列与组合

        上文中提到,完全背包在“装满背包,有几种方法”这个问题上,“先遍历背包还是先遍历物品?”十分有讲究,那么这个顺序有什么区别呢?往下看

2.5.1、组合

        //组合
        for(int i = 0; i < len; i++) { //物品
            for(int j = weight[i]; j <= maxWeight; j++) { //背包
                dp[i] = dp[i] + dp[i - weight[j]];
            }
        }

        先遍历物品再遍历背包,得到的就是一个组合数,也就是说,不需要考虑顺序;例如得到的方法中,一组数是[1, 3, 4],那么就不可能出现另一组数据为[3, 1, 4];

先遍历物品再遍历背包,就出现了以上情况,为什么?

因为我们每次是拿到物品1,然后遍历一次背包,拿到物品2,再遍历一次背包......

你就可以发现他是一个有序的过程,不会出现先拿物品2,再拿物品1的情况!

更通俗来讲: 先遍历物品的时候相当于是先把这个物品放进去了然后再看其他的能不能放进去,所以不会出现逆序。

2.5.2、排列

        //排列
        for(int j = 0; j <= maxWeight; j++) { //背包
            for(int i = 0; i < len; i++) { //物品
                if(i >= weight[j]) {
                    dp[i] = dp[i] + dp[i - weight[j]];
                }
            }
        }

        先遍历背包再遍历物品,得到的就是一个排列数,需要考虑顺序,例如得到的方法中,一组数是[1, 3, 4],那么就需要考虑出现另一组数据为[3, 1, 4]的情况;

先遍历背包再遍历物品,就出现了以上情况,为什么?

当背包的容量为1时,就遍历一次物品,容量为2时,就遍历一次物品......

因为物品排序和其重量排序不一致,所以先遍历背包不仅会出现1,2的情况,还会出现2,1这种情况;

更通俗来讲:先遍历背包相当于是用每个大小的背包看看把每一个物品都放进去一次再看别的物品能不能放进去,所以可以有逆序。

2.6、练习

建议三刷:

518. 零钱兑换 II

377. 组合总和 Ⅳ

​​​​​​322. 零钱兑换

279. 完全平方数

139. 单词拆分


        

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

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

相关文章

【2022】13 年终总结

新年Flag 2023年&#xff0c;为了各方面能有所进步&#xff0c;列一些希望达成的目标和想做的事&#xff0c;到年底看看效果。 撰写一篇英文论文 申请到CSC 和xl去外地玩两次 想到了再加 去年Flag倒了几个&#xff1f; 一维河网水动力学模型导师说不用自己编&#xff0c;看懂…

Numpy文件交互:.npy和.npz有什么区别?

文章目录saveloadsavezsavez_compressedNumpy提供了以.npy为后缀的文件存储方案&#xff0c;与这种文件格式密切相关的读、写函数分别是np.load和np.save。通过savez可以一次性存储多个数组&#xff0c;并可通过load以键值对的形式读取出来&#xff1b;如果觉得文件太大&#x…

Mybatis缓存

内存中的一块存储空间&#xff0c;服务于某个应用程序&#xff0c;旨在将频繁读取的数据临时保存在内存中&#xff0c;便于二次快速访问。 一级缓存 SqlSession级别的缓存&#xff0c;同一个SqlSession的发起多次同构查询&#xff0c;会将数据保存在一级缓存中。 注意&#x…

【NI Multisim 14.0虚拟仪器设计——放置虚拟仪器仪表(频率特性测试仪)】

目录 序言 &#x1f34d;放置虚拟仪器仪表 &#x1f349;频率特性测试仪 &#x1f34a;&#x1f34a;1.“模式”选项组 &#x1f34a;&#x1f34a;2.“水平”选项组 &#x1f34a;&#x1f34a;3.“垂直”选项组 &#x1f34a;&#x1f34a;4.“控件”选项组 序言 N…

SpringBoot+Vue项目大学生租房平台

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

数据结构课程设计[2023-01-19]

数据结构课程设计[2023-01-19] 数据结构课程设计 一、课程设计要求 实现指定的题目&#xff08;学号最后两位%41&#xff09;&#xff0c;并撰写课程设计报告。独立完成&#xff0c;功能不完备也没关系&#xff0c;只要是自己做的 使用 C、C或者 JAVA 语言&#xff0c;采用…

​第四章 Flink 窗口和水位线​

Flink 系列教程传送门 第一章 Flink 简介 第二章 Flink 环境部署 第三章 Flink DataStream API 第四章 Flink 窗口和水位线 第五章 Flink Table API&SQL 第六章 新闻热搜实时分析系统 一、时间概念&#xff1a;事件时间和处理时间 在流式处理的过程中&#xff0c;数据…

详解微信小程序开发中的“数据绑定”和代码样例

简介 首先需要区分微信小程序的运行环境和框架系统。运行环境为小程序在手机当中运行的时候&#xff0c;微信客户端所能提供的环境支持&#xff0c;也就是在这种环境下如何进行数据渲染工作&#xff1b;框架系统则是微信小程序在进行开发的过程中&#xff0c;如何通过代码实现…

数字逻辑理论——组合电路

利用数据选择器设计组合逻辑电路 m&#xff1a;组合电路输入变量个数 n&#xff1a;数据选择器的控制端个数 &#xff08;1&#xff09;mn 利用8选1数据选择器设计函数&#xff1a;FAB’A’CBC’ 待设计卡诺图&#xff1a; F∑(1,2,3,4,5,6) &#xff08;2&#xff09;m&g…

【每日一题】【LeetCode】【第十九天】【Python】汇总区间

解决之路 题目描述 测试案例&#xff08;部分&#xff09; 第一次 没有想到什么更快的方法&#xff0c;先用两个循环来写出来思路。 class Solution(object):def summaryRanges(self, nums):res []index 0n len(nums)while index < n:if index n - 1:res.append(str…

Spring_FrameWork_07(SpringMVC与SSM整合)

SpringMVC&#xff08;一种基于java实现的轻量级web框架&#xff09; 请求与响应 REST风格 SSM整合 拦截器 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {Overrideprotected WebApplicationContext createServletApplicationCont…

【工具】用AI辅助论文/博客的写作:Obsidian+Text Generator的详细安装教程

目录 前言 介绍 Obsidian Text-Generator 使用教程 安装Obsidian 安装Text Generator 插件安装 获取开放 AI API 密钥 插件选项配置 初体验 前言 对于作家、博主和学生来说&#xff0c;这是一个很好的工具&#xff0c;它通过使用最强大的语言模型之一&#xff1a;Ope…

[数据结构基础]链式二叉树的几个典型的基础oj问题

今年是农历腊月二十九&#xff0c;提前祝大家新春快乐。这是我壬寅虎年最后一篇文章&#xff0c;感谢大家的阅读。祝大家兔年吉祥&#xff0c;身体健康、阖家幸福、学业有成、事业如意、财源滚滚&#xff01; 前置说明 本文中所有用到的二叉树及二叉树节点&#xff0c;都是由…

Fabric中的txid exists问题

Fabric 默认配置中tls证书有效期为一年&#xff0c;相信挖了不少的坑&#xff0c;我前段时间写了篇文章介绍了下解决的思路&#xff0c;但是自己真解决起来还是没解决问题&#xff0c;这种分布式企业架构太复杂。 最近有遇到一个奇怪的问题&#xff0c;小伙伴写的存证数据&…

plt自定义水平线和垂直线、水平区域和垂直区域

一、添加x轴y轴垂直辅助线 1、函数 axvline函数&#xff1a;绘制垂直线。axhline函数&#xff1a;绘制水平线。 2、参数 plt.axvline(x0, ymin0, ymax1, c"g", ls"--", lw2, labelNone)。axhline类似 x&#xff1a;垂直线在x轴上的位置。浮点数&#xf…

AcWing1227.分巧克力——学习笔记

目录 题目 代码 AC结果 思路&#xff1a; 一、设置全局变量 二、获取数据 三、当前大小可得到的巧克力数是否满足每个小朋友至少分到一块 四、二分法找每个小朋友可得到的最大大小 题目 1227. 分巧克力 - AcWing题库https://www.acwing.com/problem/content/descripti…

Python---函数相关知识

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 函数前言函数的使用函数的语法格式函数的参数函…

Python脚本集成SQLite3数据库

文章目录一、Sqlite3数据库的基本使用1.数据库是什么2.数据库分类二、正式开始使用SQLite3&#xff08;Python&#xff09;1.基础sql命令2.Python连接SQLite3①查询②增加③更新一、Sqlite3数据库的基本使用 1.数据库是什么 借用百科上的话&#xff0c;数据库是“按照数据结构…

yolo v8 解决了 v5 的问题嘛?

文章大纲 yolo v8 简介网络结构yolo v8 准确率的提升yolo v8 的速度提升参考文献与学习路径yolo v8 简介 官网: https://ultralytics.com/yolov8https://github.com/triple-Mu/YOLOv8-TensorRT详细介绍: https://learnopencv.com/ultralytics-yolov8/网络结构 yolo v8 准确率…

I.MX6ULL裸机开发笔记3:SDK烧录镜像

目录 一、获取NXP官方SDK 二、Linux安装SDK 三、SDK中相关工具 四、烧录工具 一、获取NXP官方SDK 官方网站MCIMX6ULL-EVK_i.MX6ULL评估套件_NXP 半导体 二、Linux安装SDK ./XXX.run 这里如果提示sudo ./XXX.run 找不到命令&#xff0c;就可以用ls -l查看当前文件夹文件的…