背包9讲系列2-完全背包问题

一、前言

又到周末了,这几天可以腾出时间来把背包系列的其他内容好好肝一肝,本次介绍的是完全背包问题,之前的系列内容请查看:
背包9讲系列1-01背包问题

二、完全背包

2.1 问题描述

有n个物品和一个容量为capacity的背包,每种物品有无限多件,他们的体积分别为weights[i](0<=i<n),价值分别为values[i](0<=i<n),求将哪些物品装入背包可使价值总和最大?
完全背包问题与01背包问题的区别在于,01背包问题中每种物品最多只能取一次,而完全背包每种物品可以取无限次。

2.2 解体思路

2.2.1 确定状态变量(函数)

最大价值是物品数量i与背包容量j的函数,设dp[i][j]表示从前i件物品中进行选择(每件物品可以选择无限次),放入容量为j的背包所能获得的最大价值

2.2.2 确定状态转移方程(递推关系)

对于第i个物品(第1个物品体积为weights[0],第i个物品体积为weights[i-1])的选择情况如下:

  1. 如果当前背包剩余容量j<weights[i-1],则无法将该物品装入背包,此时最大价值与从前i-1个物品选择,放入容量为j的背包所能获得的最大价值相同
    dp[i][j] = dp[i-1][j]
    
  2. 如果当前背包剩余容量j>=weights[i-1],则能放入第i件物品,但是需要判断放入该物品与不放入时哪种情况所能取到的价值最大。
  • 2.1.如果第i件物品不放入背包

    dp[i][j] = dp[i-1][j]
    
  • 2.2.如果第i件物品放入背包,则背包剩余容量为j-weights[i-1],在背包9讲系列1-01背包问题中,由于每件物品最多只能取一次,所以剩余的背包容量j-weights[i-1]只能从前i-1件物品中选择并放入(第i件物品已经取过,不能再取),而完全背包的不同之处在于每种物品有无限多件可以选择,也就是说剩余的背包容量j-weights[i-1]可以从前i个物品中选择并放入(第i件物品即使取过也还可以再取),所以最大价值为:第i件物品的价值(values[i-1])+从前i件物品中选择放入容量为j-weights[i-1]的背包的最大价值(dp[i][j-weights[i-1]])

    dp[i][j] = dp[i][j-weights[i-1]] + values[i-1]
    
  • 2.3.所以当j>=weights[i-1]时,我们只需要选择两种情况下的最大值即可

    dp[i][j] = max(dp[i-1][j], dp[i][j-weights[i-1]] + values[i-1])
    
  1. 综上所述,状态转移方程如下:

    1.j<weights[i-1]: 
    	dp[i][j] = dp[i-1][j]
    2.j>=weights[i-1]
    	dp[i][j] = max(dp[i-1][j], dp[i][j-weights[i-1]] + values[i-1])
    

2.2.3 确定边界条件

  • 当背包容量为0时,无法放入任何物品到背包中,总价值为0,即dp[i][0]=0 (0<=i<=n)
  • 当不放入任何物品到背包中时,总价值也为0,即dp[0][j]=0(0<=j<=n)

2.2.4 代码示例

/**
 * @description: 背包问题-背包9讲
 * @author: Tianyi
 * @date: 2023/11/28
 */


public class KnapsackQuestion {
    /**
     * 完全背包
     * 时间复杂度:O(MN)
     * 空间复杂度:O(MN)
     *
     * @param weights  存储n件物品重量的数组,weights[i-1]表示第i件物品的重量(下标从0开始)
     * @param values   存储n件物品价值的数组,values[i-1]表示第i件物品的价值
     * @param capacity 背包的容量
     * @return 从n件物品中进行选择(每件物品可以选择无限次),放入容量为capacity的背包中所能取得的最大价值
     */
    public int knapsackComplete(int[] weights, int[] values, int capacity) {
        int n = weights.length;
        int[][] dp = new int[n + 1][capacity + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= capacity; j++) {
                if (j < weights[i - 1]) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1]);
                }
            }
        }
        return dp[n][capacity];
    }
}

三、空间优化-滚动数组

由之前的推论可知,dp[i][j]只可能通过dp[i-1][j]或dp[i][j-weights[i-1]]转移而来,设dp[j]为从前i个物品中进行选择(每种物品可以选择无限次),放入容量为j的背包所能获得的最大价值,dp[i][j]和dp[i][j-weights[i-1]]可以分别看作是外层第i次循环所获得的dp[j]和dp[j-weights[i-1]],dp[i-1][j]可看作是外层第i-1(即上一次)循环所获得的dp[j],状态转移关系如下:

1.j<weights[i-1]: 
	第i次循环得到的dp[j] = 上一次循环得到的dp[j]
2.j>=weights[i-1]
	第i次循环得到的dp[j] = max(上一次循环得到的dp[j], 第i次循环得到的dp[j-weights[i-1]] + values[i-1])

如何在更新dp[j]前保留上一次循环得到的dp[j]和本次循环得到的dp[j-weights[i-1]]?我们可以使用顺序遍历dp数组的方式,令表示容量的内层循环变量j从1增长到capacity,每次循环更新当前的dp[j],由于j-weights[i-1]肯定要小于j,所以dp[j-weights[i-1]]一定在更新dp[j]之前就已经更新了,而在当前的dp[j]还未被更新之前,dp[j]其实存储的是外层第i-1次循环得到的dp[j],这样就能满足在更新dp[j]前保留上一次循环得到的dp[j]和本次循环得到的dp[j-weights[i-1]]

for i=1; i <= n; i++
	for j=1; j <= capacity; j++
		if j<weights[i-1]:
			dp[j] = dp[j]
		else:
			dp[j] = Math.max(dp[j], dp[j-weights[i-1]]+values[i-1])

由于j<weights[i-1]时,都是用dp[j]去更新dp[j],dp[j]实际的值并没有变化,所以可以省略不用写,我们只需要考虑j>=weights[i-1]的情况,即令内层循环变量j从weights[i-1]增长到capacity,示例代码如下:

/**
 * @description: 背包问题-背包9讲
 * @author: Tianyi
 * @date: 2023/11/28
 */


public class KnapsackQuestion {
    /**
     * 完全背包-滚动数组
     * 时间复杂度:O(MN)
     * 空间复杂度:O(M)
     *
     * @param weights  存储n件物品重量的数组,weights[i-1]表示第i件物品的重量(下标从0开始)
     * @param values   存储n件物品价值的数组,values[i-1]表示第i件物品的价值
     * @param capacity 背包的容量
     * @return 从n件物品中进行选择(每件物品可以选择无限次),放入容量为capacity的背包中所能取得的最大价值
     */
    public int knapsackCompleteWithRollingArray(int[] weights, int[] values, int capacity) {
        int n = weights.length;
        int[] dp = new int[capacity + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = weights[i-1]; j <= capacity; j++) {
                dp[j] = Math.max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);
            }
        }
        return dp[capacity];
    }
}

四、结尾

本次介绍了完全背包问题的分析思路及代码示例,完全背包与01背包比较类似,需要注意的是,01背包每件物品最多取一次,在用滚动数组进行空间优化时内层循环需要倒序遍历,而完全背包每件物品有无限个,用滚动数组进行空间优化时内层循环需要顺序遍历,如果有不明白的地方欢迎评论区留言探讨。
最近会不定期的更新一系列内容,如果有疑问欢迎评论区留言探讨,感兴趣的小伙伴可以关注我的个人微信公众号,今后会陆续分享各种编程开发技术、业务场景代码设计及优化实践、问题排查解决案例、各类后端技术组件实战及原理、开发工具使用技巧及各类面试题等内容。

天逸技谈

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

误用STM32串口发送标志位 “USART_FLAG_TXE” “USART_FLAG_TC”造成的BUG

当你使用串口发送数据时是否出现过这样的情况&#xff1a; 1.发送时第一个字节丢失。 2.发送时出现莫名的字节丢失。 3.各种情况字节丢失。 1.先了解一下串口发送的流程图&#xff08;手动描绘&#xff09;&#xff1a; 可以假想USART_FLAG_TXE是用于检测"弹仓"&…

C++11——initializer_list

initializer_list的简介 initializer_list是C11新出的一个类型&#xff0c;正如类型的简介所说&#xff0c;initializer_list一般用于作为构造函数的参数&#xff0c;来让我们更方便赋值 但是光看这些&#xff0c;我们还是不知道initializer_list到底是个什么类型&#xff0c;…

关于标准库中的vector - (涉及迭代器失效,深浅拷贝,构造函数,内置类型构造函数,匿名对象)

目录 关于vector vector中的常见接口 vector常见接口的实现 迭代器失效 关于深浅拷贝 关于vector 关于vector的文档介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元…

零售数字化“逆熵”的6项原则和8种能力建设|ShopeX徐礼昭

作者&#xff1a;徐礼昭 来源&#xff1a;《三体零售逆熵法则》节选 旧的规则与秩序被打破&#xff0c;无序成为常态 新时代洪流裹挟冲击着传统零售 无序带来的“熵增”侵蚀企业生命 所有人都在不确定性中寻找确定 数字化如何助力企业铸就「反熵增」神器&#xff1f; 如何…

【交换排序 简单选择排序 堆排序 归并排序】

文章目录 交换排序简单选择排序堆排序归并排序 交换排序 冒泡排序的算法分析&#xff1a; 冒泡排序最好的时间复杂度是O&#xff08;n&#xff09;冒泡排序最好的时间复杂度是O&#xff08;n平方&#xff09;冒泡排序平均时间复杂度为O&#xff08;n的平方&#xff09;冒泡排…

spring boot定时器实现定时同步数据

文章目录 目录 文章目录 前言 一、依赖和目录结构 二、使用步骤 2.1 两个数据源的不同引用配置 2.2 对应的mapper 2.3 定时任务处理 总结 前言 一、依赖和目录结构 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifa…

无线物理层安全学习

文章目录 3.17到3.203.85到3.88 3.17到3.20 3.85到3.88

#计算机毕业设计#微信小程序#社区团购#小猪优选

小猪优选 简介 类似于美团优选&#xff0c;多多买菜等线上平台。 是一套社区团购的项目&#xff0c;是依托真实社区的一种区域化、小众化、本地化、网络化的团购形式&#xff0c;同事也是一种生鲜商品流通的新零售模式。 背景&#xff1a; 社区团购是真实具名团体的一种互…

电脑提示mfc100u.dll缺失如何解决?分享有效的5个解决方法

由于各种原因&#xff0c;电脑可能会出现一些问题&#xff0c;其中之一就是电脑提示mfc100u.dll的错误。这个问题可能会导致电脑无法正常运行某些程序或功能。为了解决这个问题&#xff0c;我将分享验证有效的五个修复方法&#xff0c;帮助大家恢复电脑的正常运行。 首先&#…

SALib敏感性分析入门实践笔记

1. 敏感性分析 敏感性分析是指从定量分析的角度研究有关因素发生某种变化对某一个或一组关键指标影响程度的一种不确定分析技术。 其实质是通过逐一改变相关变量数值的方法来解释关键指标受这些因素变动影响大小的规律。 敏感性因素一般可选择主要参数&#xff08;如销售收入、…

Redis数据结构之跳表

跳表是一种有序的数据结构&#xff0c;它通过在每个节点中维持多个指向其他节点的指针&#xff0c;从而达到快速访问节点的目的。其核心思想就是通过建立多级索引来实现空间换时间。 在Redis中&#xff0c;使用跳表作为Zset的一种底层实现之一&#xff0c;这也是跳表在Redis中的…

IO延迟引起的虚拟机故障排查

vmware 虚拟机连上之后总感觉非常卡&#xff0c;查看CPU 内存资源使用率是正常的。 message 日志有cpu卡住的报错 NMI watchdog: BUG: soft lockup - CPU#8 stuck for 23s! [container-31451:45878]下面分析是什么导致的服务器cpu卡住。 1、打开prometheus&#xff0c;观察服务…

CSS新手入门笔记整理:CSS图片样式

图片大小 语法 width:像素值; height:像素值; 图片边框&#xff1a;border 语法 边框&#xff1a;宽度值 样式值 颜色值&#xff1b; border:1px solid red; 图片对齐 水平对齐&#xff1a;text-align 语法 text-align:取值; 属性值 说明 left 左对齐(默认值) cent…

神经网络 代价函数

神经网络 代价函数 首先引入一些便于稍后讨论的新标记方法&#xff1a; 假设神经网络的训练样本有 m m m个&#xff0c;每个包含一组输入 x x x和一组输出信号 y y y&#xff0c; L L L表示神经网络层数&#xff0c; S I S_I SI​表示每层的neuron个数( S l S_l Sl​表示输出…

[英语学习][5][Word Power Made Easy]的精读与翻译优化

[序言] 今日完成第18页的阅读, 发现大量的翻译错误以及不准确. 需要分两篇文章进行讲解. [英文学习的目标] 提升自身的英语水平, 对日后编程技能的提升有很大帮助. 希望大家这次能学到东西, 同时加入我的社区讨论与交流英语相关的内容. [原著英文与翻译版对照][第18页] Wh…

基于SpringBoot的企业客户管理系统的设计与实现

摘 要 本论文主要论述了如何使用JAVA语言开发一个企业客户管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述企业客户管理系统的当前背景以及系统开发的目…

STM32---MDK工程创建

本节我们带领大家学习如何新建一个寄存器库版本MDK的详细步骤&#xff1b; 由于51单片机的学习时&#xff0c;所涉及的寄存器很少&#xff0c;所以往往几个头文件、驱动文件就可以完成相关的功能&#xff0c;但是对于STM32来讲&#xff0c;涉及的寄存器、头文件等都很多&#…

CleanMyMac X4.16.2最新2024注册许可证

都说苹果的闪存是金子做的&#xff0c;这句话并非空穴来风&#xff0c;普遍都是256G起步&#xff0c;闪存没升级一个等级&#xff0c;价格都要增加上千元。昂贵的价格让多数消费者都只能选择低容量版本的mac。而低容量的mac是很难满足用户的需求的&#xff0c;伴随着时间的推移…

005、简单页面-容器组件

之——布局 目录 之——布局 杂谈 正文 1.布局基础知识 2.Column 3.Row 4.实践 杂谈 布局容器组件。 一个丰富的页面需要很多组件组成&#xff0c;那么&#xff0c;我们如何才能让这些组件有条不紊地在页面上布局呢&#xff1f;这就需要借助容器组件来实现。 容器组件是…

elment Loading 加载组件动态变更 text 值bug记录

先上效果图: 倒计时4分钟组件方法 // 倒计时 4分钟getSencond() {this.countDown 4分00秒this.interval setInterval(() > {this.maxTime--;let minutes Math.floor(this.maxTime / 60);let seconds Math.floor(this.maxTime % 60);minutes minutes < 10 ? 0 minu…