动态规划-----背包类问题(0-1背包与完全背包)详解
目录
什么是背包问题?
动态规划问题的一般解决办法:
0-1背包问题:
0 - 1背包类问题 分割等和子集:
完全背包问题:
完全背包类问题 零钱兑换II:
什么是背包问题?
背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkle和Hellman提出的。
动态规划问题的一般解决办法:
动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们得需要一些变量来保存,一般是用一维数组或者二维数组来保存。下面我们先来讲下做动态规划题很重要的三个步骤:
- 🧐 步骤一:定义dp数组元素的含义
- 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
- 🧐第三步骤:找出初始值(base case)
接下来的题目我们会按照这三个步骤来解释说明
前言:本文包含动态规划中的经典背包问题,有关背包问题的描述如下:
在动态规划中,背包问题是一个经典的优化问题,它可以分为0-1背包问题和完全背包问题两种类型。下面我们就来看看这两个问题:
0-1背包问题:
问题描述:
给你一个可装载重量为 W
的背包和 N
个物品,每个物品有重量和价值两个属性。其中第 i
个物品的重量为 wt[i]
,价值为 val[i]
。现在让你用这个背包装物品,每个物品只能用一次,在不超过被包容量的前提下,最多能装的价值是多少?
- 🧐 步骤一:定义dp数组元素的含义:
由于状态有两个,就是「背包的容量」和「可选择的物品」,这里我们就需要用到一个二维的dp
数组,如下为dp数组的定义:
🦉🦉🦉dp[i][w]
的定义如下:对于前 i
个物品,当前背包的容量为 w
,这种情况下可以装的最大价值是 dp[i][w]
- 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
1.如果你没有把这第 i
个物品装入背包,那么很显然,最大价值 dp[i][w]
应该等于 dp[i-1][w]
,继承之前的结果(翻译一下就是不装入第i个物品,相当于对前 i - 1 个物品进行选择,对应此时的背包容量w)。即此时的状态转移方程是:dp[ i ][ w ] = dp[ i - 1 ][ w ]
2.如果你把这第 i
个物品装入了背包,此时背包剩余容量为 w - wt[ i - 1 ](wt数组下标是从0开始的, wt[ i - 1 ] 相当于第 i 个物品的重量,val 也一样)
则此时的状态转移方程是:dp[ i ][ w ] = dp[ i - 1 ][ w - wt[ i - 1] ] + val[ i - 1 ]
- 🧐第三步骤:找出初始值(base case):
这题的base case 相对简单,当物品个数为0或则背包当前容量为0时,dp[ i ][ w ] 都等于0
按照上述的状态转移方程,我们可以填出对应dp表格(以图中的例子为例):
有了上述铺垫后,动态规划的代码就很好实现了,具体代码如下:
int knapsack(int W, int N, int[] wt, int[] val) {assert N == wt.length;// base case 已初始化,数组自动全部初始化为0int[][] dp = new int[N + 1][W + 1];for (int i = 1; i <= N; i++) {for (int w = 1; w <= W; w++) {if (w - wt[i - 1] < 0) {// 这种情况下只能选择不装入背包dp[i][w] = dp[i - 1][w];} else {// 装入或者不装入背包,择优dp[i][w] = Math.max(dp[i - 1][w - wt[i-1]] + val[i-1], dp[i - 1][w]);}}}return dp[N][W];
}
有了上面的一定了解后,我们来看看0 - 1背包的类似题:
0 - 1背包类问题 分割等和子集:
看一下力扣第 416 题「分割等和子集open in new window」:
题目描述:输入一个只包含正整数的非空数组 nums
,请你写一个算法,判断这个数组是否可以被分割成两个子集,使得两个子集的元素和相等。对应函数签名如下:
我们可以将这个问题转化为0 - 1背包问题,具体做法:
这个问题相当于给一个可装载重量为 sum / 2
的背包和 N
个物品,每个物品的重量为 nums[i]
。现在让你装物品,是否存在一种装法,能够恰好将背包装满?按照上述解题思路就是:
- 🧐 步骤一:定义dp数组元素的含义:
dp[i][j] = x
表示,对于前 i
个物品(i
从 1 开始计数),当前背包的容量为 j
时,若 x
为 true
,则说明可以恰好将背包装满,若 x
为 false
,则说明不能恰好将背包装满。
- 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
与上面类似,这里就直接给出来了:
1.不把这第 i
个物品装入背包:dp[ i ][ j ] = dp[ i - 1 ][ j ]
2.把这第i个物品装入背包:dp[ i ][ j ] = dp[ i - 1][ j - nums[ i - 1 ] ]
- 🧐第三步骤:找出初始值(base case):
当背包容量为0时(sum / 2= 0)这时无论物体有多少个都可以满足条件,就是什么都不装嘛
ok,接下来看完整代码:
boolean canPartition(int[] nums) {int sum = 0;for (int num : nums) sum += num;// 和为奇数时,不可能划分成两个和相等的集合if (sum % 2 != 0) return false;int n = nums.length;sum = sum / 2;boolean[][] dp = new boolean[n + 1][sum + 1];// base casefor (int i = 0; i <= n; i++)dp[i][0] = true;for (int i = 1; i <= n; i++) {for (int j = 1; j <= sum; j++) {if (j - nums[i - 1] < 0) {// 背包容量不足,不能装入第 i 个物品dp[i][j] = dp[i - 1][j];} else {// 装入或不装入背包dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];}}}return dp[n][sum];
}
完全背包问题:
完全背包问题与0-1背包问题类似,但不同之处在于每个物品可以选择放入背包多次(数量无限),即每个物品的选择是一个无限的选择。我们给出对应的解题方法:
- 🧐 步骤一:定义dp数组元素的含义:
若只使用前 i
个物品(可以重复使用),当背包容量为 j
时,能装入背包的最大价值为dp[i][w]
- 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
1.不将第i个物品装入背包,此时:dp[ i ][ w ] = dp[ i - 1 ][ w ]
2.将第i个物品装入背包,此时:dp[ i ][ w ] = dp[ i ][ w -wt[ i - 1] ] + val[ i - 1 ]
- 🧐第三步骤:找出初始值(base case):
这题与0 - 1的bas背包的base case 一致,当物品个数为0或者背包当前容量为0时,dp[ i ][ w ] 都等于0
对应的动态规划代码为:
int fullBackpack(int W, int N, int[] wt, int[] val) {assert N == wt.length;// base case 已初始化,数组自动全部初始化为0int[][] dp = new int[N + 1][W + 1];for (int i = 1; i <= N; i++) {for (int w = 1; w <= W; w++) {if (w - wt[i - 1] < 0) {// 这种情况下只能选择不装入背包dp[i][w] = dp[i - 1][w];} else {// 装入或者不装入背包,择优dp[i][w] = Math.max(dp[i][w - wt[i-1]] + val[i-1], dp[i - 1][w]);}}}return dp[N][W];
}
完全背包类问题 零钱兑换II:
这是力扣第 518 题「零钱兑换 IIopen in new window」,题目描述:
给定不同面额的硬币 coins
和一个总金额 amount
,写一个函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。对应函数签名如下:
我们可以把这个问题转化为完全背包问题的描述形式:
有一个背包,最大容量为 amount
,有一系列物品 coins
,每个物品的重量为 coins[i]
,每个物品的数量无限。请问有多少种方法,能够把背包恰好装满?按照动态规划三步走:
- 🧐 步骤一:定义dp数组元素的含义:
dp[i][j]
的定义如下:若只使用前 i
个物品(可以重复使用),当背包容量为 j
时,有 dp[i][j]
种方法可以装满背包。
- 🧐步骤二:找出数组元素之间的关系式(也就是我们所熟知的状态转移方程)
1.如果你不把这第 i
个物品装入背包,也就是说你不使用 coins[i-1]
这个面值的硬币,那么凑出面额 j
的方法数 dp[i][j]
应该等于 dp[i-1][j]
,继承之前的结果
即状态转移方程为:dp[ i ][ j ] = dp[ i -1 ][ j ]
2.如果你把这第 i
个物品装入了背包,也就是说你使用 coins[i-1]
这个面值的硬币,那么 dp[i][j]
应该等于 dp[i][j-coins[i-1]]
。
即状态转移方程为:dp[ i ][ j ] = dp[ i ][ j - coins[ i - 1] ]
- 🧐第三步骤:找出初始值(base case):
base case 为 dp[0][..] = 0, dp[..][0] = 1
。i = 0
代表不使用任何硬币面值,这种情况下显然无法凑出任何金额;j = 0
代表需要凑出的目标金额为 0,那么什么都不做就是唯一的一种凑法。
写出动态规划代码:
int change(int amount, int[] coins) {int n = coins.length;int[][] dp = int[n + 1][amount + 1];// base casefor (int i = 0; i <= n; i++) dp[i][0] = 1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= amount; j++)//装入背包或者不装入,加起来if (j - coins[i-1] >= 0)dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i-1]];else // < 0 只能选择不装入背包dp[i][j] = dp[i - 1][j];}return dp[n][amount];
}
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固自己的知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!
相关文章:

动态规划-----背包类问题(0-1背包与完全背包)详解
目录 什么是背包问题? 动态规划问题的一般解决办法: 0-1背包问题: 0 - 1背包类问题 分割等和子集: 完全背包问题: 完全背包类问题 零钱兑换II: 什么是背包问题? 背包问题(Knapsack problem)是一种…...
通过 Docker 搭建 BookStack
文章目录 环境说明1、官方网站2、通过 Docker 部署总结 环境说明 操作系统版本:CentOS Linux release 7.9.2009 (Core) Docker 版本:Docker Engine - Community 24.0.2 BookStack 版本:23.02.3 MySQL 版本:8.0.32 1、官方网站 G…...
通俗易懂:什么是Java虚拟机(JVM)?它的主要作用是什么?
Java虚拟机(Java Virtual Machine, JVM)是一种软件实现的抽象计算机,它负责执行Java字节码(Bytecode)。Java程序并不是直接在物理计算机上运行,而是先由Java编译器将源代码编译成与平台无关的字节码&#x…...
[k8s] kubectl执行失败后等待一段时间再重试 (Shell实现)
使用Shell脚本实现功能: kubectl执行失败后,等待30秒后再重试,一共重试3次,代码如下: #!/bin/bashKUBECTL_BIN/var/lib/snapd/snap/bin/kubectlERR_MSG_K8S_NOTRUNNING"microk8s is not running" ERR_MSG_C…...
java中的static和单例模式
同一个类中,访问其类成员,可以省略类名不写 static:叫静态,可以修饰成员变量,成员方法。 成员变量按照有无static修饰,分为两种: 类变量:有static修饰,属于类…...
RabbitMQ相关总结
Broker 异步调用中用Broker进行事件订阅和调用,完成解耦 没有强依赖,不用担心级联失败 流量削峰 MQ 的下载 1.可以使用命令拉取镜像 docker pull rabbitmq:3-management 2.也可以直接去官网下载tar包,然后上传到虚拟机上面 spring AMQP…...

RAFT: Adapting Language Model to Domain Specific RAG
今天来介绍下伯克利大学3.15日新发的一篇paper,RAFT: Adapting Language Model to Domain Specific RAG 主要研究了如何构造训练数据来微调你的LLM,从而在LLM在垂直领域的RAG中表现更好。并且开源了代码:GitHub - ShishirPatil/gorilla: Gorilla: An API store for LLMs 主…...

第十五届蓝桥杯第三期模拟赛第十题 ← 上楼梯
【问题描述】 小蓝要上一个楼梯,楼梯共有 n 级台阶(即小蓝总共要走 n 级)。小蓝每一步可以走 a 级、b 级或 c 级台阶。 请问小蓝总共有多少种方案能正好走到楼梯顶端?【输入格式】 输入的第一行包含一个整数 n 。 第二行包含三个整…...
第四题:星期一
题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 整个 20 世纪(1901 年 1 月 1 日至 2000 年 12 月 31 日之间),一共有多少个星期一?(不要告诉我你不知道今天是星期几…...

Mamba: Linear-Time Sequence Modeling with Selective State Spaces(论文笔记)
What can I say? 2024年我还能说什么? Mamba out! 曼巴出来了! 原文链接: [2312.00752] Mamba: Linear-Time Sequence Modeling with Selective State Spaces (arxiv.org) 原文笔记: What: Mamba: Linear-Time …...
2024蓝桥杯每日一题(区间DP)
备战2024年蓝桥杯 -- 每日一题 Python大学A组 试题一:游戏 试题二:石子合并 试题三:密码脱落 试题四:能量项链 试题一:游戏 【题目描述】 玩家一和玩家二共同玩一个小游戏。给定一个包含 N 个…...
LeetCode-2952. 需要添加的硬币的最小数量【贪心 数组 排序】
LeetCode-2952. 需要添加的硬币的最小数量【贪心 数组 排序】 题目描述:解题思路一:看提示主要是用贪心和排序。那我们肯定是首先对coins排序。然后依次遍历coins[i],获取当前可以获取金额范围,和判断是否加入新硬币。判断规则如下…...

新书速递——《可解释AI实战(PyTorch版)》
本书旨在帮助你实施最新的可解释AI技术,以构建公平且可解释的AI系统。可解释AI是当今AI研究中的热门话题,但只有少数资源和指南涵盖了所有重要技术,这些技术对实践者来说非常有价值。本书旨在填补这一空白。 本书读者对象 本书既适合那些有兴…...

国产数据库中统计信息自动更新机制
数据库中统计信息描述的数据库中表和索引的大小数以及数据分布状况,统计信息的准确性对优化器选择执行计划时具有重要的参考意义。本文简要整理了下传统数据库和国产数据库中统计信息的自动更新机制,以加深了解。 1、数据库统计信息介绍 优化器是数据库…...

【C++】入门C++(中)
好的,我们继续,这是 C专栏的第二篇博客,还没读过上一篇博客可以进入我创建的专栏阅读 入门C(上)再回来哦~ 下面我们要讲的第一个概念就是函数重载 函数重载 1. 函数重载概念 什么是函数重载? 简单来说…...

javaIO
file类 一个File类的对象可以表示一个具体的文件或目录 mkdir 创建单级文件夹 mkdirs 创建多级文件夹 delete 删除一个文件夹时,文件夹里面必须是空的 listfiles 将文件夹的子集放到一个file类型的数组中 输入及输出的概念 输入input 输出output 把jav…...

睿尔曼超轻量仿人机械臂之复合机器人底盘介绍及接口调用
机器人移动平台是一个包含完整成熟的感知、认知和定位导航能力的轮式机器人底盘产品级平台,产品致力于为各行业细分市场的商用轮式服务机器人提供一站式移动机器人解决方案,让合作伙伴专注在核心业务/人机交互的实现。以下是我司产品双臂机器人以及复合升…...

用JSch实现远程传输文件并打包成jar
本文将简单介绍一下 JSch 这个Java的第三方库的一个简单用法,并以此为实例,讲解 IntelliJ 中打包成 jar 包的2种方式。 实现目标 我们的目标是,做出一个jar包,它能够实现类似于 scp 命令的远程传输文件的功能。用法如下…...

2023年第十四届蓝桥杯大赛软件类省赛C/C++研究生组真题(代码完整题解)
C题-翻转⭐ 标签:贪心 简述:如果 S 中存在子串 101 或者 010,就可以将其分别变为 111 和 000,操作可以无限重复。最少翻转多少次可以把 S 变成和 T 一样。 链接: 翻转 思路:要求步骤最少->S每个位置最多修改一次->从头开始遍历不匹配就翻转->翻转不了就-1 …...

力扣刷题Days28-第二题-11.盛水最多的容器(js)
目录 1,题目 2,代码 3,学习与总结 3.1思路回顾 1,如何遍历 2,算法流程 3.2剖析问题 1,题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, h…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...