代码随想录刷题记录day37 0-1背包+分割等和子集

news2025/1/12 12:06:14

代码随想录刷题记录day37 0-1背包+分割等和子集

0-1背包

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

例题:

背包最大重量为4。

物品为:

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

问背包能背的物品最大价值是多少?

分析:

  • 二维数组

动态规划五步曲

  1. 确定dp数组的含义

dp[i][j] 表示从下标为0-i的物品随便取,放进容量为j的背包的最大的价值。

可以有两个方向推导过来。

不放物品i

dp[i][j]=dp[i-1][j],表示从0到i-1中任意选取,放到容量为j的背包的最大的价值。

放物品i

dp[i][j]=dp[i-1][j-weight[i]]+value[i]。表示从0到i-1中任意选,放到容量为j-weight[i]的背包,加上第i个的价值

  1. 确定递推公式

    由上可得

    dp[i][j]=max(dp[i-1][j] , dp[i-1][j-weight[i]]+value[i])

  2. 初始化

    dp[i][0],背包容量为0得时候都是0,

    又i是由i-1状态所得的,所以需要对dp[0][j]初始化,dp[0][j]表示背包容量为j,只有物品0可装的最大价值。

    所以当容量j大于物品0所需的容量时,dp[0][j]=value[0],

    否则dp[0][j]=0

    初始化代码如下所示

    for (int j = 0 ; j < weight[0]; j++) { 
        //j<weight[0] 表示背包容量j小于物品0的重量
        dp[0][j] = 0;
    }
    // 正序遍历
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[0];
    }
    

    初始化后的二维数组

在这里插入图片描述

  1. 确定遍历顺序

    先遍历背包顺序还是先遍历物品的顺序呢?二维dp数组都是可以的

    比如先遍历物品顺序

    for(int i=1;i<weight.length;i++){
        for(int j=1;j<=bagsize;j++){
            if (j < weight[i]) dp[i][j] = dp[i - 1][j]; 
            //如果背包的容量小于物品i的重量,
            else dp[i][j]=Math.max(dp[i-1][j] , dp[i-1][j-//从物品数量开始遍历 物品从1-2 物品1已经初始化过了   为什么要初始化 因为 动态规划方程中 用到了  i-1 所以物品0一开始的时候就要初始化
            // 背包重量从1-4 如果背包重量是0的话 价值就为0了 已经初始过了 就不用再继续初始化了
            for (int i = 1; i < weight.length; i++) {
                for (int j = 1; j < bagsize + 1; j++) {
                    //如果容量小于当前物品的重量 也就是说当前i号物品 是放不下背包的 必须得加上这一步的判断 因为j-weight[i]可能为负数
                    if(j<weight[i])
                        dp[i][j]=dp[i-1][j];
                    else
                        dp[i][j]=Math.max(dp[i-1][j-weight[i]]+value[i],dp[i-1][j]);//判断是把这个物品放进去价值大 还是不放进去价值大
    
                }
            }weight[i]]+value[i])
        }
    }
    

    先遍历背包容量也可,因为所需要的二维数组是由上方和左边推导出来的。

    5.打印数组

在这里插入图片描述

  • 一维数组

    也叫做滚动数组,当前数组的值依赖于上一个数组

动态规划五步曲:

  1. 一维数组dp[j]的定义

    表示背包容量为j,能装下最大的价值。

  2. 递推公式

    dp[j]可由dp[j-weight[i]]推导而来

    dp[j-weight[i]]表示背包容量为j-weight[i]能放下的最大的价值

    dp[j]的选择有两个

    • 一个是装物品i,dp[j]=dp[j - weight[i]] + value[i]
    • 一个是不装物品,dp[j]=dp[j]

    所以递推公式

    dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    
  3. 初始化

    dp[0]=0表示背包容量为0的能放下最大的价值就是0

    dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。

    这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了

  4. 遍历顺序

    先遍历物品,再遍历背包,且背包需要从后往前遍历

    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[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);

    举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15

    如果正序遍历

    dp[1] = dp[1 - weight[0]] + value[0] = 15

    dp[2] = dp[2 - weight[0]] + value[0] = 30

    此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。

    为什么倒序遍历,就可以保证物品只放入一次呢?

    倒序就是先算dp[2]

    dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)

    dp[1] = dp[1 - weight[0]] + value[0] = 15

    所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。

    为什么先遍历背包再遍历物品不行

    如果先遍历背包在遍历物品

    for (int j = bagsize; j >= 1; j--) {
        for (int i = 0; i < wLen; i++) {
            if (j < weight[i]) dp[j] = dp[j];
            else
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }
    

    只会选择一个物品,因为背包倒叙遍历的原因,前面的还没有初始化,dp数组为 0 15 15 20 30

  5. dp数组打印

    0 15 15 20 35

代码

    public static void testBagProblem2(int [] weight ,int[] value, int bagsize){
        //初始化二维数组 一共有3个物品
        int wLen = weight.length;
        //定义dp数组:dp[j]表示背包容量为j时,能获得的最大价值
        int[] dp = new int[bagsize + 1];
        //遍历顺序:先遍历物品,再遍历背包容量
        for (int i = 0; i < wLen; i++){
            for (int j = bagsize; j >= weight[i]; j--){
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }

//        for (int j = bagsize; j >= 1; j--) {
//            for (int i = 0; i < wLen; i++) {
//                if (j < weight[i]) dp[j] = dp[j];
//                else
//                    dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
//            }
//        }

        //打印dp数组
        for (int j = 0; j <= bagsize; j++){
            System.out.print(dp[j] + " ");
        }

    }

416. 分割等和子集

在这里插入图片描述

思想

套用01背包

背包的容量:sum/2
物品 nums,其中nums中的数值既表示重量又表示价值。

递归五步曲

  1. dp数组的含义

    dp[j] 表示容量为j的背包,能装下的最大的价值

  2. 递推公式

    dp[j]=max(dp[j],dp[j-weight[i]]+value[i]) 加物品i和不加物品i

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

    nums[i] 既表示物品i的重量也表示物品i的价值

  3. 初始化

    dp[0]=0;因为有取最大值,所以其他的也取为0

  4. 遍历顺序,先遍历物品在遍历背包

    for(int i=0;i<nums.length;i++){//先遍历物品
    for(int j=target;j>=nums[i];j–){//为什么j>=nums[i] 因为当j<nums[i]时,不可能放物品 dp[j]=0;
    dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
    }
    }

  5. 打印数组

    nums = [1,5,11,5]
    
    下标	0 1 2 3 4 5 6 7 8 9 10 11
    dp=	{0,1,1,1,1,5,6,6,6,6,10,11}
    

如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j,理解这一点很重要。

如何理解这句话:

背包容量为j,装的最大的价值正好为j,背包是由物品填装的,背包中的物品正好把背包给填满了,背包中的物品表示一个子集。

代码

class Solution {
    public boolean canPartition(int[] nums) {
        //可以当作0 1背包问题来处理
        //背包的容量:sum/2
        //物品 nums,其中nums中的数值既表示重量又表示价值。
        
        //dp数组的定义
        //dp[j]表示容量为j的背包,能装下的最大的价值

        //递推公式
        //dp[j]=max(dp[j],dp[j-weight[i]]+value[i]) 加物品i和不加物品i
        //应用到这道题目
        //dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]) nums[i] 既表示物品i的重量也表示物品i的价值

        //遍历
        int sum=0;
        for(int num:nums){
            sum+=num;
        }
        if (sum % 2 == 1) return false;
        int target=sum/2;
        int [] dp=new int[target+1];
        for(int i=0;i<nums.length;i++){//先遍历物品
            for(int j=target;j>=nums[i];j--){
                dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        System.out.println(Arrays.toString(dp));
        if(dp[target]==target) return true;
        

        return false;
    }
}

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

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

相关文章

操作系统实验五 进程间通信-管道通信

实验目的 1.掌握利用管道机制实现进程间的通信的方法 2.了解利用消息缓冲队列机制实现进程间的通信的方法 3..了解利用共享存储区机制实现进程间的通信的方法 五个题目如下 1. 函数int pipe(int fd[2])创建一个管道&#xff0c;管道两端可分别用描述字fd[0]以及fd[1]来描述。需…

多元宇宙算法求解电力系统多目标优化问题(Matlab实现)【电气期刊论文复现与创新】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;本科计算机专业&#xff0c;研究生电气学硕…

SpringCloud项目使用Nacos进行服务的注册

本篇介绍Spring cloud项目使用Nacos作为注册中心来进行服务注册及服务发现&#xff0c;并进行简单的测试来验证。 一、简介 nacos是一个集服务发现、服务配置、服务元数据以及流量管理于一体的管理中心&#xff0c;能帮助我们更好的发现、配置和管理微服务。 注意&#xff1…

家政公司网站毕业设计,家政服务系统设计与实现,毕业设计论文源码开题报告需求分析

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于web网页的家政服务预约系统&#xff0c;整个网站项目使用了B/S架构&#xff0c;基于java的springboot框架下开发&#xff1b;管理员通过后台录入信息、管理信息&#xff0c;设置网站信息&#xff0c;管理…

JS项目打包之ROLLUP.JS入门

一、目的 Rollup是一个用于JavaScript的模块打包器&#xff0c;它将小块代码编译成更大、更复杂的东西&#xff0c;例如库或应用程序。它为JavaScript ES6版本中包含的代码模块使用了新的标准化格式&#xff0c;而不是以前的特殊解决方案&#xff0c;如CommonJS和AMD。ES模块可…

Win10安装Nacos

Win10安装Nacos 文章目录Win10安装Nacos前言下载Nacos安装Nacos验证前言 最近在学微服务的东西&#xff0c;使用的是 Spring Cloud Alibaba 生态&#xff0c;Nacos就是其中关键的一环。 这是 Nacos 的官网地址&#xff1a;https://nacos.io/zh-cn/index.html 官网的文档对于…

Python中用PyTorch机器学习神经网络分类预测银行客户流失模型

分类问题属于机器学习问题的类别&#xff0c;其中给定一组特征&#xff0c;任务是预测离散值。分类问题的一些常见示例是&#xff0c;预测肿瘤是否为癌症&#xff0c;或者学生是否可能通过考试。 最近我们被客户要求撰写关于银行客户流失的研究报告&#xff0c;包括一些图形和…

@Scheduled定时任务搭配Redis防止多实例定时重复调用

有个Redis安装使用教程&#xff0c;可视化界面&#xff0c;有需要的话&#xff0c;可以打开这个链接去看一下 https://blog.csdn.net/weixin_45507672/article/details/105973279 创建个maven项目&#xff0c;在pom.xml文件加上以下依赖 <dependency><groupId>or…

4EVERLAND专用网关公告,免费体验

我们很高兴地宣布发布 4EVERLAND 专用 IPFS 网关&#xff01;与 4EVERLAND 公共网关一起&#xff0c;4EVERLAND 专用网关将为全世界的开发者和用户提供更快、更稳定地访问更能体现其品牌形象的 IPFS 内容。 专用网关的好处&#xff1a; 全球分布的边缘节点提供全球加速无速率…

[附源码]计算机毕业设计快转二手品牌包在线交易系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Spring Boot 入门到精通(二)

文章目录五、SpringBoot整合MyBatis5.1 mapper 配置5.2 mapper映射配置&#xff1a;配置文件方式5.3 注解配置方式六. 自定义部分SpringMvc配置。6.1 SpringBoot整合日期转换器6.1.1 配置原理6.1.2 日期转换器整合6.2 SpringBoot整合拦截器七. Spring Boot 自定义日志配置&…

C++11特性-类的改进

1.构造函数 1.委托构造函数&#xff1a;允许同一个类的构造函数调用其他构造函数&#xff0c;简化变量初始化 class BB { public:BB() {}BB(int max) {this->m_max max > 0 ? max : 100;cout << "max " << this->m_max << endl;}BB(i…

【ATF】bootloader与安全相关启动分析

这个文章的内容不只是指的ATF启动这个部分&#xff0c;其实ATF是TF-A&#xff0c;这个是一个启动框架&#xff0c;所以今天我们来看看bootloader这部分的启动代码。后续继续补充&#xff01;&#xff01;&#xff01; 第一部分参考的内容来自&#xff1a;https://mp.weixin.qq…

美食餐厅网站毕业设计,餐厅座位预定系统设计与实现,毕业设计怎么写论文毕设源码开题报告需求分析怎么做

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于java的公司企业网站&#xff0c;整体基于B/S架构&#xff0c;技术上使用基于java的springboot框架来实现&#xff1b;通过后台添加公司资讯、公司产品、公司产品案例、查看注册用户、查看留言等&#xf…

【无人机分配】一种无人机实时最优任务分配模型附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

解决Android嵌套H5,自定义控制页面能否实现下拉刷新功能

目录使用场景问题描述思路分析解决方案结语使用场景 关于Android嵌套H5页面使用下拉刷新功能的问题&#xff1a;当我们在Android中使用第三方框架SmartRefreshLayout实现App的下拉刷新功能时&#xff0c;如果H5页面有部分功能设计到上下滑动的话&#xff0c;就会引起“误触”下…

Jenkins Kubernetes 应用部署与容器构建

Jenkins & Kubernetes 应用部署与容器构建 文章目录Jenkins & Kubernetes 应用部署与容器构建1. 前言2. Jenkins 配置 kubernetes credentials3. Jenkins 插件3.1 安装 Kubernets Plugin3.2 安装 Docker Plugin3.3 安装 Git Plugin4. Jenkins 连接 minikube 集群5. 配置…

MQ高级(一)消息可靠性

消息从生产者发送到exchange&#xff0c;再到queue&#xff0c;再到消费者&#xff0c;有哪些导致消息丢失的可能性&#xff1f; &#xff08;1&#xff09;发送时丢失&#xff1a; 1️⃣生产者发送的消息未送达exchange 2️⃣消息到达exchange后未到达queue &#xff08;2&…

【物理应用】基于傅里叶伽辽金谱法二维纳维-斯托克斯附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

[附源码]计算机毕业设计教育企业网站Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…