【动态规划】落花人独立,微雨燕双飞 - 8. 01背包问题

news2025/1/23 11:11:23

在这里插入图片描述

本篇博客给大家带来的是01背包问题之动态规划解法技巧.
🐎文章专栏: 动态规划
🚀若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

  • 要开心
    • 要快乐
      • 顺便进步
  • 1. 01背包
  • 2. 分割等和子集

要开心

要快乐

顺便进步

1. 01背包

题目链接: DP41 【模板】01背包

题目内容:
描述
在这里插入图片描述

示例1
输入:
3 5
2 10
4 5
1 4

输出:
14
9

说明:
装第一个和第三个物品时总价值最大,但是装第二个和第三个物品可以使得背包恰好装满且总价值最大。
示例2
输入:
3 8
12 6
11 8
6 8

输出:
8
0

说明:
装第三个物品时总价值最大但是不满,装满背包无解。
备注:
要求O(nV)的时间复杂度,O(V)空间复杂度

第一Ⅰ问的动态规划

1. 状态表示
在这里插入图片描述

01背包问题本质上还是 线性dp问题,按线性dp来定义状态即可.
第Ⅰ问: 尝试一维定义
dp[i] 表示在前 i 个位置中选择物品, 在所有的选法中, 物品总体积不超过背包容量V 时的最大价值.
上述一维定义在写状态转移方程时不能保证所选物品体积不会超过背包的容量,并且选完之后背包剩余容量也时未知的.一维定义解决不了问题,
用二维定义dp[i][j] 表示在前 i 个位置中选择物品, 物品总体积不超过 V的所有选法中能选出来的最大价值.

2. 状态转移方程
根据最后一个位置是否选择, 来划分问题:
① i 物品不选, dp[i] = dp[i-1][j]
② i 物品要选, 需要保证剩余容量大于等于0, j-v[i]>= 0, w[i] + dp[i-1][j-v[i]];

3. 初始化
选择物品是从下标1 开始的, 到n结束, 那么dp表就需要多创建一行一列,处理两个细节问题:
①下标之间的对应关系: i -> i j -> j
②初始化虚拟节点:
第一行表示的是 物品可选,总体积不超过 j 的最大价值. 既然没有物品,那就不选,全都为0.
第一列表示的是 所选物品总体积不超过0的最大价值. 此时最大价值就是0,不选即可,全都为0.

在这里插入图片描述

4. 填表顺序
根据状态转移方程可知,要想得到dp[i][j] 就得先知道dp[i-1][j]或者dp[i-1][j-v[i]],
所以应该从上往下填写每一行
从左往右填写每一列.

5. 返回值
打印 dp[n][V]即可.

第二 Ⅱ问的动态规划

1. 状态表示
在这里插入图片描述

与第Ⅰ问同样的分析方法:
二维定义dp[i][j] 表示在前 i 个位置中选择物品, 物品总体积刚好为 V的所有选法中能选出来的最大价值.
可能存在所有选法都不能恰好装满背包,我们规定此时dp[i][j] = -1;

2. 状态转移方程
根据最后一个位置是否选择, 来划分问题:
① i 物品不选, dp[i] = dp[i-1][j]
② i 物品要选, 需要保证剩余容量大于等于0, j-v[i]>= 0, w[i] + dp[i-1][j-v[i]];

3. 初始化
选择物品是从下标1 开始的, 到n结束, 那么dp表就需要多创建一行一列,处理两个细节问题:
①下标之间的对应关系: i -> i j -> j
②初始化虚拟节点:
第一行表示的是 没有物品可选,总体积等于 j 的最大价值.dp[0][0] = 0, 后面 无论选不选,总价值都达不到 j ,于是全都初始化为-1.
第一列表示的是 所选物品总体积等于0的最大价值. 此时最大价值就是0,不选即可,全都为0.

在这里插入图片描述

4. 填表顺序
根据状态转移方程可知,要想得到dp[i][j] 就得先知道dp[i-1][j]或者dp[i-1][j-v[i]],
所以应该从上往下填写每一行
从左往右填写每一列.

5. 返回值
打印 dp[n][V]即可.

第三 优化

①利用滚动数组做空间上的优化
在这里插入图片描述

在这里插入图片描述
②直接在原始代码上修改
Ⅰ删除所有的 i 维
(★)Ⅱ 填表时从右往左遍历 j .

第四 代码实现

// //优化前:
        //  Scanner in = new Scanner(System.in);
        // int N = 1010;
        // // 注意 hasNext 和 hasNextLine 的区别
        // int n = in.nextInt();
        // int V = in.nextInt();
        // int[][] dp1 = new int[N][N];
        // int[][] dp2 = new int[N][N];
        // int[] v = new int[N];
        // int[] w = new int[N];
        // for (int i = 1; i <= n; ++i) {
        //     v[i] = in.nextInt();
        //     w[i] = in.nextInt();
        // }
        // //1.解决第一个问题(1)求这个背包至多能装多大价值的物品?
        // //填表
        // for (int i = 1; i <= n; ++i) {
        //     for (int j = 0; j <= V; ++j) { //修改遍历顺序
        //      dp1[i][j] = dp1[i-1][j];
        //      if(j >= v[i]) {
        //         dp1[i][j] = Math.max(dp1[i][j], w[i] + dp1[i-1][j - v[i]]);
        //      }
        //     }
        // }
        // System.out.println(dp1[n][V]);
        // //2. 解决第二个问题 (2)若背包恰好装满,求至多能装多大价值的物品?
        // for (int i = 1; i <= V; ++i) {
        //     dp2[0][i] = -1;
        // }
        // for (int i = 1; i <= n; ++i) {
        //     for (int j = 0; j <= V; ++j) {
        //         dp2[i][j] = dp2[i-1][j];
        //         if (j-v[i] >= 0 && dp2[i-1][j - v[i]] != -1) {
        //             dp2[i][j] = Math.max(dp2[i][j], w[i] + dp2[i-1][j - v[i]]);
        //         }
        //     }
        // }
        // //若是-1则输出0, 若不是则正常输出.
        // System.out.println((dp2[n][V] == -1 ? 0 : dp2[n][V]));

        //优化后:
        Scanner in = new Scanner(System.in);
        int N = 1010;
        // 注意 hasNext 和 hasNextLine 的区别
        int n = in.nextInt();
        int V = in.nextInt();
        int[] dp1 = new int[N];
        int[] dp2 = new int[N];
        int[] v = new int[N];
        int[] w = new int[N];
        for (int i = 1; i <= n; ++i) {
            v[i] = in.nextInt();
            w[i] = in.nextInt();
        }
        //1.解决第一个问题(1)求这个背包至多能装多大价值的物品?
        //填表
        for (int i = 1; i <= n; ++i) {
            for (int j = V; j >= v[i]; --j) { //修改遍历顺序
                dp1[j] = Math.max(dp1[j], w[i] + dp1[j - v[i]]);
                
            }
        }
        System.out.println(dp1[V]);
        //2. 解决第二个问题 (2)若背包恰好装满,求至多能装多大价值的物品?
        for (int i = 1; i <= V; ++i) {
            dp2[i] = -1;
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = V; j >= v[i]; --j) {
                if (dp2[j - v[i]] != -1) {
                    dp2[j] = Math.max(dp2[j], w[i] + dp2[j - v[i]]);
                }
            }
        }
        //若是-1则输出0, 若不是则正常输出.
        System.out.println((dp2[V] == -1 ? 0 : dp2[V]));

2. 分割等和子集

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

题目内容:
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

提示:

1 <= nums.length <= 200
1 <= nums[i] <= 100

第一 预处理

直接去判断两个子集的元素和是否相等会有点复杂, 可以转化为只研究一个子集等于原数组元素和的一半, 即在数组中选择一些数, 这些数的和是否等于原数组元素和的一半.

第二 动态规划

1. 状态表示
根据第一题模板题可知定义状态的方式:
dp[i][j]表示在nums的前 i 个位置中选择一些数,所有选法中,是否存在一些数,使得这些数之和为 j.

2. 状态转移方程
根据最后一个位置nums[i]是否选择来划分情况:
①nums[i]不选: dp[i][j] = dp[i-1][j];
②nums[i]要选: 需要保证所选数之和不超过 j . j - nums[i] >= 0, dp[i][j-nums[i]];

3. 初始化
dp表多创建一行一列,需要处理两个问题:
①dp表与nums表元素之间的对应关系:
i -> i-1; j -> j-1
②初始化虚拟节点(就是多创建的一行一列),保证填表的正确性:

第一行 i=0意味着没有元素可选,怎么选最大值都只能是0,所以dp[0][0] = true,其余全部初始化为false.
第一列 j=0意味着需要凑成和为0, 直接不选即可. 故从i=1开始全部初始化为true.

在这里插入图片描述

4. 填表顺序
根据状态转移方程可知,要想求得dp[i][j]就得先知道dp[i-1][j]和dp[i-1][j-nums[i]],所以可知填表顺序为:
从上往下填写每一行
每一行从左往右填写

5. 返回值
返回dp[nums.length][原数组元素和的一半]

第三 优化

①利用滚动数组做空间上的优化
在这里插入图片描述

在这里插入图片描述
②直接在原始代码上修改
Ⅰ删除所有的 i 维
(★)Ⅱ 填表时从右往左遍历 j .

第四 代码实现

class Solution {
    public boolean canPartition(int[] nums) {
    //     int n = nums.length;
    //     int sum = 0;
    //     for(int x : nums) sum += x;
    //     if(sum%2 == 1) return false;
    //     int aim = sum/2;
    //     boolean[][] dp = new boolean[n+1][aim+1];
    //     for(int i = 0;i <= n;++i) {
    //         dp[i][0] = true;
    //     }
    
    //     for(int i = 1;i <= n;++i) {
    //         if(i < n) {
    //             sum += nums[i];
    //         }
    //         for(int j = 1;j <= aim;++j) {
    //             dp[i][j] = dp[i-1][j];
    //             if(j >= nums[i-1] && dp[i][j] == false) {
    //                 dp[i][j] = dp[i-1][j-nums[i-1]];
    //             }
    //         }
    //     }
    //     return dp[n][aim];
    // }

    //优化: 删除第一维, 从右往左遍历第二维
    int n = nums.length;
        int sum = 0;
        for(int x : nums) sum += x;
        if(sum%2 == 1) return false;
        int aim = sum/2;
        boolean[] dp = new boolean[aim+1];
        dp[0] = true;
    
        for(int i = 1;i <= n;++i) {
            if(i < n) {
                sum += nums[i];
            }
            for(int j = aim;j >= nums[i-1];--j) { //修改遍历顺序,本质上是滚动数组的优化.
                
                if(dp[j] == false) {
                    dp[j] = dp[j-nums[i-1]];
                }
            }
        }
        return dp[aim];
    }
}

本篇博客到这里就结束啦, 感谢观看 ❤❤❤

🐎期待与你的下一次相遇😊😊😊

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

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

相关文章

记一次虚机上传过慢问题排查

最近线上虚机有个特殊的用户反馈&#xff0c;用户反馈虚机从A服务器下载文件特别慢&#xff0c;于是scp A服务器数据到本地client&#xff0c;发现 只有几十K的流量。 当时第一反应怀疑是虚机负载压力比较大&#xff0c;但是查看虚机IO以及负载都很低。。。。 然后tcpdump抓包发…

web服务器 网站部署的架构

WEB服务器工作原理 Web web是WWW(World Wide Web)的简称&#xff0c;基本原理是&#xff1a;请求(客户端)与响应(服务器端)原理&#xff0c;由遍布在互联网中的Web服务器和安装了Web浏览器的计算机组成 客户端发出请求的方式&#xff1a;地址栏请求、超链接请求、表单请求 …

数据结构——实验七·排序

嗨~~欢迎来到Tubishu的博客&#x1f338;如果你也是一名在校大学生&#xff0c;正在寻找各种编程资源&#xff0c;那么你就来对地方啦&#x1f31f; Tubishu是一名计算机本科生&#xff0c;会不定期整理和分享学习中的优质资源&#xff0c;希望能为你的编程之路添砖加瓦⭐&…

Windows系统提示RunDLL PcaWallpaperAppDetect错误修复方法

最近&#xff0c;Win11 24H2预览版和Win10 LTSC 2025功能更新偶尔会触发RunDLL错误弹窗 具体表现为 //英文提示 Error in C:\WINDOWS\system32\PcaSvc.dll Missing entry: PcaWallpaperAppDetect//中文提示 C:\WINDOWS\system32\PcaSvc.dll出错 丢失条目:PcaWallpaperAppDe…

计算机组成原理——数据表示(二)

当生活的压力和困惑缠绕在身边&#xff0c;我们往往需要振奋精神&#xff0c;勇往直前。无论在何种困境中&#xff0c;我们都要保持积极的态度和坚定的信念。将悲观的情绪抛之脑后&#xff0c;展现出坚强的意志力和无尽的活力。振奋精神意味着我们要战胜自己内心的负面情绪&…

人源化抗体的改造方式及其优势【卡梅德生物】

随着生物制药行业的迅速发展&#xff0c;抗体药物已经成为治疗多种疾病&#xff08;尤其是癌症、免疫性疾病等&#xff09;的重要手段。抗体人源化改造技术作为抗体药物研发的关键技术之一&#xff0c;在提高药物疗效和降低免疫原性方面发挥了至关重要的作用。 1. 人源化抗体的…

【Linux】深刻理解动静态库

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统载…

【java数据结构】其他非基于比较排序

【java数据结构】其他非基于比较排序 一、计数排序二、基数排序三、桶排序 博客最后附有整篇博客的全部代码&#xff01;&#xff01;&#xff01; 一、计数排序 场景&#xff1a;集中在某个范围内的一组数据 思路&#xff1a; 找到这组序列的最大值和最小值&#xff0c;通过…

博客之星2024年度总评选——我的年度创作回顾与总结

2024年&#xff0c;是我在CSDN博客上持续耕耘、不断成长的一年。在此&#xff0c;与大家分享一下我的年度创作回顾与总结。 一、创作成长与突破 在人工智能领域&#xff0c;技术迭代迅速&#xff0c;知识更新频繁。为了保持自己的竞争力&#xff0c;在今年&#xff0c;我始终…

ChromeOS 132 版本更新

ChromeOS 132 版本更新 1. 企业定制化 Chrome Web Store 管理员现在可以使用新设置定制 Chrome Web Store 以适应他们管理的用户&#xff0c;包括以下功能&#xff1a; 添加公司标志添加首页横幅和自定义公告策划扩展集合实施基于类别的控制 这些设置可以通过管理员控制台进…

Golang Gin系列-5:数据模型和数据库

在这篇Gin教程的博客中&#xff0c;我们将探索如何将模型和数据库与Gin框架无缝集成&#xff0c;使你能够构建健壮且可扩展的web应用程序。通过利用流行的库并遵循最佳实践&#xff0c;你将学习如何定义模型、建立数据库连接、执行CRUD操作以及确保基于gin的项目中的数据完整性…

计算机毕业设计hadoop+spark股票基金推荐系统 股票基金预测系统 股票基金可视化系统 股票基金数据分析 股票基金大数据 股票基金爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

设计模式的艺术-代理模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解代理模式 代理模式&#xff08;Proxy Pattern&#xff09;&#xff1a;给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。 代理模式类型较多…

Spring Boot整合Thymeleaf、JDBC Template与MyBatis配置详解

本文将详细介绍如何在Spring Boot项目中整合Thymeleaf模板引擎、JDBC Template和MyBatis&#xff0c;涵盖YAML配置、依赖版本匹配、项目结构设计及代码示例。 一、版本兼容性说明 Spring Boot版本与Java版本对应关系 Spring Boot 2.x&#xff1a;支持Java 8、11&#xff08;推…

【博客之星】2024年度创作成长总结 - 面朝大海 ,春暖花开!

没关系的&#xff0c;大家都会做错选择&#xff0c;会 莫名其妙掉眼泪&#xff0c;走在路上会突然崩溃&#xff0c; 但这并不影响我们去看看晚霞&#xff0c; 再次爱上这个世界。 面朝大海 &#xff0c;春暖花开! about meReviewLife about me 现在我是一名24级计算机类的…

StyleMaster: Stylize Your Video with Artistic Generation and Translation 论文解读

目录 一、概述 二、相关工作 1、图像风格化 2、视频风格化 三、StyleMaster 1、创建对比数据集 2、提取全局描述子 3、局部描述和全局描述结合 4、时间和风格质量的运动适配器 5、Gray Tile ControlNet 四、实验 一、概述 Our StyleMaster demonstrates superior vi…

c++进阶---c++三大特性之一---多态

多态的简单介绍&#xff1a;是一种动态的访问函数&#xff0c;比如&#xff1a;你定义了一个一个人类和一个学生类&#xff0c;当你传入的是学生类的时候&#xff0c;你需要有购物优惠&#xff0c;这种情境下用多态就很适用。 1.简单的多态使用&#xff1a; 1.1构造多态的条件…

安卓程序作为web服务端的技术实现(二):Room 实现数据存储

已经实现web服务器安卓程序作为web服务端的技术实现&#xff1a;AndServer 实现登录权限拦截-CSDN博客 现在需要和正常web项目类似&#xff0c;那么就需要操作数据库 一般web项目都是选择较为重型的数据库如MySQL&#xff0c;SQL server等 这里是安卓项目&#xff0c;我目前…

如何使用Python脚本将本地项目上传到 GitHub

前言 这里我们通过创建一个新的github仓库&#xff0c;来测试我们的脚本能否上传我们本地的项目&#xff0c;并且进行更新。首先你需要先安装 Git&#xff0c;关于这部分我好像没有记录过&#xff0c;这里我搜索看了一下&#xff0c;这篇博客写的Git安装详解应该是比较齐全的&…

Day 15 卡玛笔记

这是基于代码随想录的每日打卡 222. 完全二叉树的节点个数 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#x…