当前位置: 首页 > news >正文

动态规划算法之背包问题详细解读(附带Java代码解读)

动态规划中的背包问题(Knapsack Problem)是经典问题之一,通常用来解决选择一组物品放入背包使得背包的价值最大化的问题。根据问题条件的不同,背包问题有很多种变体,如0-1背包问题完全背包问题多重背包问题等。这里,我们详细介绍最经典的0-1背包问题,并提供代码的详细解读。

1. 0-1背包问题简介

在0-1背包问题中,有一个容量为 C 的背包和 n 件物品。每件物品有两个属性:重量 w[i]价值 v[i]。目标是选择若干件物品放入背包,使得总重量不超过 C,并且背包中物品的总价值最大化。

问题的约束:
  1. 每件物品要么选择(放入背包),要么不选择,因此称为 0-1 背包问题。
  2. 背包的总重量不能超过 C
  3. 要最大化背包中物品的总价值。

2. 动态规划的思路

动态规划适用于背包问题,因为它具有最优子结构重叠子问题的性质。解决这个问题的核心在于:针对每一个物品,都有两种选择——要么放进背包,要么不放进背包。

状态定义

定义 dp[i][j] 表示前 i 件物品放入容量为 j 的背包时所能获得的最大总价值。

状态转移方程

对于每件物品 i

  1. 如果不将第 i 件物品放入背包,则最大价值就是 dp[i-1][j],即前 i-1 件物品的最大价值。
  2. 如果将第 i 件物品放入背包,则最大价值为 dp[i-1][j-w[i]] + v[i],即前 i-1 件物品在剩余容量 j-w[i] 时的最大价值加上当前物品的价值 v[i]

综合起来,状态转移方程为:

dp[i][j]=max(dp[i−1][j],dp[i−1][j−w[i]]+v[i])

初始条件

当背包容量为0时,无论选哪件物品,最大价值都是0,即 dp[i][0] = 0

3. Java代码实现

public class Knapsack {public static void main(String[] args) {// 定义物品的重量和价值int[] weights = {2, 3, 4, 5}; // 每个物品的重量int[] values = {3, 4, 5, 6};  // 每个物品的价值int capacity = 8; // 背包容量int n = weights.length; // 物品数量// 计算并输出背包的最大价值System.out.println("Maximum value in Knapsack = " + knapsack(weights, values, n, capacity));}// 动态规划方法解决0-1背包问题public static int knapsack(int[] weights, int[] values, int n, int capacity) {// 定义DP数组:dp[i][j] 表示前 i 件物品在容量为 j 时的最大价值int[][] dp = new int[n + 1][capacity + 1];// 填充DP表for (int i = 1; i <= n; i++) {for (int j = 1; j <= capacity; j++) {if (weights[i - 1] <= j) { // 当前物品可以放入背包dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);} else { // 当前物品无法放入背包dp[i][j] = dp[i - 1][j];}}}// 返回最后一个格子的值,即最大价值return dp[n][capacity];}
}

4. 详细解释代码

4.1 输入和输出

main 方法中,定义了物品的重量数组 weights[]、价值数组 values[],以及背包的总容量 capacity 和物品数量 n。然后调用 knapsack() 方法来计算背包中可以获得的最大价值。

4.2 knapsack() 函数详解
public static int knapsack(int[] weights, int[] values, int n, int capacity) {// 定义DP数组:dp[i][j] 表示前 i 件物品在容量为 j 时的最大价值int[][] dp = new int[n + 1][capacity + 1];

首先,定义了二维数组 dp[][],其中 dp[i][j] 表示前 i 件物品在背包容量为 j 时能够获得的最大总价值。数组的大小为 [n+1][capacity+1],因为我们需要处理物品数量从0到n、容量从0到capacity的所有情况。

4.3 初始化和状态转移
for (int i = 1; i <= n; i++) {for (int j = 1; j <= capacity; j++) {if (weights[i - 1] <= j) {dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);} else {dp[i][j] = dp[i - 1][j];}}
}

接下来,用两个嵌套的 for 循环遍历物品和背包容量,进行状态转移:

  • 外层循环 i 遍历每一件物品。
  • 内层循环 j 遍历背包的每个可能容量。

对于每个物品 i

  • 如果该物品的重量 weights[i-1] 小于或等于当前容量 j,可以选择放入背包或不放入背包,选择价值最大的方案。这就是通过状态转移方程 dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1]) 计算出的。
  • 如果该物品的重量 weights[i-1] 超过当前容量 j,则无法将其放入背包,此时只能继承前 i-1 件物品的最大价值,即 dp[i][j] = dp[i-1][j]
4.4 返回最大价值
return dp[n][capacity];

循环结束后,dp[n][capacity] 就是前 n 件物品在背包容量为 capacity 时能够获得的最大价值,因此返回该值。

5. 复杂度分析

  • 时间复杂度O(n * capacity),因为我们需要填充一个大小为 n * capacity 的表。
  • 空间复杂度O(n * capacity),因为我们使用了二维数组 dp[][] 存储中间结果。

6. 空间优化

在上述实现中,我们用了二维数组 dp[][] 来存储所有状态。但实际上,在每一行 i 的状态转移时,只依赖于上一行 i-1 的值。因此,我们可以将二维数组压缩为一维数组,从而降低空间复杂度到 O(capacity)

public static int knapsackOptimized(int[] weights, int[] values, int n, int capacity) {int[] dp = new int[capacity + 1];for (int i = 1; i <= n; i++) {// 从大到小遍历容量,保证每个物品只被计算一次for (int j = capacity; j >= weights[i - 1]; j--) {dp[j] = Math.max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);}}return dp[capacity];
}
关键点:
  • 在每次迭代中,从容量 capacity 开始递减遍历,确保每个物品只更新一次。这种写法防止了在同一轮次中更新状态值时物品被重复选择。

7. 总结

0-1背包问题通过动态规划求解,有明确的状态转移方程。使用二维DP表来记录每个物品在不同容量下的最大价值,最终得到最优解。通过压缩空间,可以进一步优化到一维DP表。

相关文章:

动态规划算法之背包问题详细解读(附带Java代码解读)

动态规划中的背包问题&#xff08;Knapsack Problem&#xff09;是经典问题之一&#xff0c;通常用来解决选择一组物品放入背包使得背包的价值最大化的问题。根据问题条件的不同&#xff0c;背包问题有很多种变体&#xff0c;如0-1背包问题、完全背包问题、多重背包问题等。这里…...

Vue3+TypeScript二次封装axios

安装如下 npm install axios 第一步&#xff1a;创建config配置文件&#xff0c;用于存放请求后端的ip地址&#xff0c;用于后期打包后便于修改ip地址。 注&#xff1a;typescript要求参数要有类型。&#xff08;ES6 定义对象 属性 类型 修改的是属性的值&#xff09; inte…...

华为 HCIP-Datacom H12-821 题库 (16)

有需要题库的可以加下方Q群 V群进行学习交流 1. OSPF 邻居关系建立出现故障&#xff0c;通过 display ospf error 命令来检查&#xff0c;输出结果如图所示&#xff0c;根据图中内容分析&#xff0c;邻居建立失败的原因可能是以下哪一项&#xff1f; A、Process ID 不一致 B、…...

【论文分享精炼版】 sNPU: Trusted Execution Environments on Integrated NPUs

今天在COMPASS分享了之前写的一个博客&#xff0c;做了进一步的提炼总结&#xff0c;大家可以看看原文~ 今天分享的论文《sNPU: Trusted Execution Environments on Integrated NPUs》来自2024年ISCA&#xff0c;共同一作为Erhu Feng以及Dahu Feng。并且&#xff0c; 这两位作…...

MyBatis 入门之动态 SQL

文章目录 一、什么是动态 SQL二、MyBatis 中的动态 SQL 标签三、动态 SQL 的使用示例四、总结 在 Java 开发中&#xff0c;MyBatis 是一个非常流行的持久层框架&#xff0c;它提供了强大的 SQL 映射功能和动态 SQL 支持。动态 SQL 可以根据不同的条件动态地生成 SQL 语句&#…...

软工大二学生待办事项:

该文章会常年更新!坚持! 2024.9.10 学习打包部署 记录睡眠 开始刷一个算法 巩固Git版本控制工具的使用 巩固利用Idea使用版本管理工具,SQl编写 抓紧时间了解公司营业执照 坚持到周末再打瓦!...

MongoDB延迟查询

在 MongoDB 中&#xff0c;查看副本集成员之间的副本延迟可以通过以下步骤进行&#xff1a; 使用 rs.status() 命令&#xff1a; 这个命令提供了副本集的详细状态信息&#xff0c;包括每个成员的延迟情况。在 MongoDB shell 中&#xff0c;你可以执行以下命令&#xff1a; rs.s…...

python如何获取html中的所有链接

在Python中&#xff0c;获取HTML页面中的所有链接通常可以通过使用第三方库如BeautifulSoup或lxml来完成。这里&#xff0c;我将提供一个使用BeautifulSoup库的示例&#xff0c;因为它简单易用且功能强大。 首先&#xff0c;你需要安装BeautifulSoup和requests库&#xff08;如…...

79-java static修饰的类能不能被继承

Java中的类可以被final关键字修饰&#xff0c;表示这个类不能被继承。如果一个类被final修饰&#xff0c;那么这个类不能被继承&#xff0c;也就是说&#xff0c;final类不能被继承。 另一方面&#xff0c;static关键字可以用来修饰内部类&#xff0c;这样的内部类是静态内部类…...

MacOS wine中文乱码问题

安装wine 1、brew update 执行失败&#xff0c;提示安装如下 2、git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow 3、git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow 3、brew update 4、brew in…...

基于Springboot的鲜花销售网站的设计与实现

项目描述 这是一款基于Springboot的鲜花销售网站的系统 模块描述 鲜花销售系统 1、用户 登录 在线注册 浏览商品 鲜花搜索 订购商品 查询商品详情 水果分类查看 水果加购物车 下单结算 填写收货地址 2、管理员 登录 用户管理 商品管理 订单管理 账户管理 截图...

安卓玩机工具-----适合安卓机型的“搞机工具箱” 功能齐全 玩机推荐

搞机工具箱最新版是一款相当出色的电脑端手机工具箱软件&#xff0c;搞机工具箱正式版功能强劲&#xff0c;可以帮助用户不需要root就能够直接对手机进行调节&#xff0c;方便对手机进行更加全面的掌控&#xff0c;搞机工具箱便捷好用&#xff0c;只需要根据文字提示及自己的需…...

数据分析-17-时间序列分析的平稳性检验

1 什么是时间序列 时间序列是一组按时间顺序排列的数据点的集合,通常以固定的时间间隔进行观测。这些数据点可以是按小时、天、月甚至年进行采样的。时间序列在许多领域中都有广泛应用,例如金融、经济学、气象学和工程等。 时间序列的分析可以帮助我们理解和预测未来的趋势和…...

Unity3D Android多渠道极速打包方案详解

在移动应用开发过程中&#xff0c;特别是在使用Unity3D进行Android游戏或应用开发时&#xff0c;多渠道打包是一个常见且重要的需求。不同的渠道&#xff08;如Google Play、华为应用市场、小米应用商店等&#xff09;可能需要不同的配置和包名&#xff0c;手动进行这些操作既耗…...

数据库中的主键和外键分别是什么意思?

让我们来聊聊数据库设计中非常重要的两个概念——主键&#xff08;Primary Key&#xff09;和外键&#xff08;Foreign Key&#xff09;。这两个概念对于保证数据的一致性和完整性至关重要。 主键&#xff08;Primary Key&#xff09; 主键是一个表中的一个或一组字段&#x…...

HTML5中`<ul>`标签深入全面解析

在HTML5的广阔天地里&#xff0c;<ul>标签作为无序列表的代言人&#xff0c;扮演着举足轻重的角色。它不仅能够整洁地罗列信息&#xff0c;还通过丰富的属性和样式选项&#xff0c;为网页设计师提供了无限的创意空间。本文将深入剖析<ul>标签的内核&#xff0c;详细…...

MongoDB日志级别

日志 查看当前的日志级别 根据你提供的 MongoDB 命令结果&#xff0c;命令 db.adminCommand({ getParameter: "logComponentVerbosity" }) 返回了 "ok" : 0&#xff0c;这意味着命令执行失败&#xff0c;没有成功获取到日志级别的配置信息。错误信息 &quo…...

Softmax回归--分类--有监督

输出和类别的维度一样。 一、当我们想将先线性层的输出直接视为概率&#xff0c;存在一些问题&#xff1a; 1.不能限制输出数字总和为1。 2.不能保证都是正数。 所以会使用softmax进行归一化。 二、交叉熵损失 交叉熵是一个衡量两个概率分布之间差异的很好的度量&#xff0…...

Jenkins生成html报告

下载插件 1.需要下载插件 html Publisher plugins 2.下载Groovy(设置css样式&#xff09;&#xff0c;默认没有css样式 在Job配置页面&#xff0c;增加构建步骤Execute system Groovy script&#xff0c;在Groovy Command中输入上面命令&#xff0c;即可&#xff1a; System.…...

牛客——查找字符串

B-你好&#xff0c;这里是牛客竞赛_牛客周赛 Round 59 (nowcoder.com) 返回值是子串或字符在 string 对象字符串中的位置 #include <bits/stdc.h> using namespace std; int T; string s; int main() { cin >> T; while(T --) { cin >>…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...