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

完全背包问题 - 动态规划最优解法(Java实现)

完全背包问题 - 动态规划最优解法(Java实现)

问题描述

完全背包问题是01背包问题的扩展版本:

  • 有一个容量为 capacity 的背包
  • n 种物品,每种物品有重量 weights[i] 和价值 values[i]
  • 关键区别:每种物品可以选择无限次(0次、1次、2次…)
  • 目标:在不超过背包容量的前提下,使背包中物品总价值最大

与01背包的核心区别

特性01背包完全背包
物品使用限制每个物品最多用1次每个物品可用无限次
状态转移dp[i-1][w-weight] + valuedp[w-weight] + value
关键差异从上一行取值从当前行已计算的值取值

最推荐的Java解决方案

public static int completeKnapsack(int[] weights, int[] values, int capacity) {int n = weights.length;// dp[w]表示容量为w时能获得的最大价值int[] dp = new int[capacity + 1];// 对每个容量进行计算for (int w = 1; w <= capacity; w++) {// 尝试每种物品for (int i = 0; i < n; i++) {int weight = weights[i];int value = values[i];if (weight <= w) {// 当前物品重量不超过容量,可以选择dp[w] = Math.max(dp[w], dp[w - weight] + value);}// 物品重量超过容量时,跳过(隐式else分支)}}return dp[capacity];
}

关键变量详解

变量含义作用
dp[w]容量为w时能获得的最大价值状态定义,存储子问题的最优解
n物品种类总数确定内层循环的范围
capacity背包容量确定dp数组大小和外层循环范围
weight当前考虑物品的重量判断是否能放入当前容量的背包
value当前考虑物品的价值计算选择该物品后的总价值增量
w当前考虑的背包容量外层循环控制变量,逐步求解各容量下的最优解
i当前考虑的物品索引内层循环控制变量,遍历所有物品种类

算法核心思想

关键洞察:由于每个物品可以用无限次,当我们计算 dp[w] 时,可以利用已经计算好的 dp[w-weight],这个值可能已经包含了当前物品的使用,因此实现了"无限次使用"的效果。

完整可视化演示

使用测试用例(精心设计以覆盖所有代码分支):

  • 物品信息:[重量: [1,3,4], 价值: [15,20,30]]
  • 背包容量:4

初始化阶段

DP数组初始状态:
dp[0] = 0  dp[1] = 0  dp[2] = 0  dp[3] = 0  dp[4] = 0

逐步计算过程

第1轮:计算 dp[1](容量=1)
当前容量:1
尝试物品:

物品1 (重量=1, 价值=15):

  • weight=1 <= w=1 ✓ → 分支1:可以选择
  • 不选:dp[1] = 0
  • 选择:dp[1-1] + 15 = dp[0] + 15 = 0 + 15 = 15
  • dp[1] = Math.max(0, 15) = 15

物品2 (重量=3, 价值=20):

  • weight=3 > w=1隐式else分支:跳过

物品3 (重量=4, 价值=30):

  • weight=4 > w=1隐式else分支:跳过
第1轮完成:dp[1] = 15
当前状态:[0, 15, 0, 0, 0]
第2轮:计算 dp[2](容量=2)
当前容量:2
尝试物品:

物品1 (重量=1, 价值=15):

  • weight=1 <= w=2 ✓ → 分支1:可以选择
  • 不选:dp[2] = 0
  • 选择:dp[2-1] + 15 = dp[1] + 15 = 15 + 15 = 30
  • dp[2] = Math.max(0, 30) = 30选择分支胜出

物品2 (重量=3, 价值=20):

  • weight=3 > w=2隐式else分支:跳过

物品3 (重量=4, 价值=30):

  • weight=4 > w=2隐式else分支:跳过
第2轮完成:dp[2] = 30
当前状态:[0, 15, 30, 0, 0]
解释:容量2时,选择2个物品1(2×15=30)
第3轮:计算 dp[3](容量=3)
当前容量:3
尝试物品:

物品1 (重量=1, 价值=15):

  • weight=1 <= w=3 ✓ → 分支1:可以选择
  • 当前:dp[3] = 0
  • 选择:dp[3-1] + 15 = dp[2] + 15 = 30 + 15 = 45
  • dp[3] = Math.max(0, 45) = 45

物品2 (重量=3, 价值=20):

  • weight=3 <= w=3 ✓ → 分支1:可以选择
  • 当前:dp[3] = 45
  • 选择:dp[3-3] + 20 = dp[0] + 20 = 0 + 20 = 20
  • dp[3] = Math.max(45, 20) = 45不选择分支胜出

物品3 (重量=4, 价值=30):

  • weight=4 > w=3隐式else分支:跳过
第3轮完成:dp[3] = 45
当前状态:[0, 15, 30, 45, 0]
解释:容量3时,选择3个物品1(3×15=45)比选择1个物品2(1×20=20)更优
第4轮:计算 dp[4](容量=4)
当前容量:4
尝试物品:

物品1 (重量=1, 价值=15):

  • weight=1 <= w=4 ✓ → 分支1:可以选择
  • 当前:dp[4] = 0
  • 选择:dp[4-1] + 15 = dp[3] + 15 = 45 + 15 = 60
  • dp[4] = Math.max(0, 60) = 60

物品2 (重量=3, 价值=20):

  • weight=3 <= w=4 ✓ → 分支1:可以选择
  • 当前:dp[4] = 60
  • 选择:dp[4-3] + 20 = dp[1] + 20 = 15 + 20 = 35
  • dp[4] = Math.max(60, 35) = 60不选择分支胜出

物品3 (重量=4, 价值=30):

  • weight=4 <= w=4 ✓ → 分支1:可以选择
  • 当前:dp[4] = 60
  • 选择:dp[4-4] + 30 = dp[0] + 30 = 0 + 30 = 30
  • dp[4] = Math.max(60, 30) = 60不选择分支胜出
最终完成:dp[4] = 60
最终状态:[0, 15, 30, 45, 60]

详细计算过程可视化表格

容量w物品1(1,15)物品2(3,20)物品3(4,30)最终dp[w]最优选择
0---0
1✓选择(0+15=15)✗跳过✗跳过151个物品1
2✓选择(15+15=30)✗跳过✗跳过302个物品1
3✓选择(30+15=45)✗不选(45>20)✗跳过453个物品1
4✓选择(45+15=60)✗不选(60>35)✗不选(60>30)604个物品1

代码分支覆盖分析

通过上述演示用例,我们完全覆盖了Java代码的所有分支:

分支1:weight <= w(可以选择)

  • 触发场景:物品1在所有容量下;物品2在容量3,4时;物品3在容量4时
  • 代码执行dp[w] = Math.max(dp[w], dp[w - weight] + value)
  • 说明:当前物品可以放入背包,需要比较选择和不选择哪个更优

隐式else分支:weight > w(不能选择)

  • 触发场景:物品2在容量1,2时;物品3在容量1,2,3时
  • 代码行为:跳过当前物品,不执行任何操作
  • 说明:当前物品太重,无法放入背包

Math.max的比较分支

选择物品更优的情况:
  • dp[1] = max(0, 15) = 15:选择物品1
  • dp[2] = max(0, 30) = 30:选择2个物品1
  • dp[3] = max(0, 45) = 45:选择3个物品1
  • dp[4] = max(0, 60) = 60:选择4个物品1
不选择物品更优的情况:
  • dp[3]考虑物品2时:max(45, 20) = 45,保持原值
  • dp[4]考虑物品2时:max(60, 35) = 60,保持原值
  • dp[4]考虑物品3时:max(60, 30) = 60,保持原值

算法复杂度

  • 时间复杂度:O(n × capacity),需要对每个容量尝试每种物品
  • 空间复杂度:O(capacity),只需要一维dp数组

最终答案与最优方案

通过动态规划,我们得到最优解是 60,对应的最优选择方案是:

  • 选择4个物品1(每个重量=1,价值=15)
  • 总重量:4×1=4 ≤ 4 ✓
  • 总价值:4×15=60

算法优势

这个解法具有以下优势:

  1. 空间优化:相比二维DP,只需要一维数组
  2. 逻辑清晰:外层遍历容量,内层遍历物品
  3. 易于理解:状态转移方程简单直观
  4. 高效实用:时空复杂度都是最优的

与01背包的对比总结

特性01背包完全背包
问题本质每个物品至多选1次每个物品可选无限次
DP定义dp[i][w]:前i个物品,容量wdp[w]:容量w的最大价值
状态转移dp[i-1][w-weight]dp[w-weight]
核心区别从上一行取值从当前行取值
空间复杂度O(n×W)O(W)

完全背包通过修改状态转移的取值位置,巧妙地实现了物品的重复使用,是动态规划思想的精彩体现。


文章转载自:

http://GRQaHqVI.qwgct.cn
http://ak3krqbc.qwgct.cn
http://ttTKlQDA.qwgct.cn
http://EDXkzAfa.qwgct.cn
http://RVGixvqK.qwgct.cn
http://jRFwYBxt.qwgct.cn
http://ULvyBJzT.qwgct.cn
http://eYReFgXQ.qwgct.cn
http://pMtime5Z.qwgct.cn
http://M2Sf5tAy.qwgct.cn
http://5sQfrHzA.qwgct.cn
http://WhrijvY6.qwgct.cn
http://44PPkgFF.qwgct.cn
http://NjRQOT5K.qwgct.cn
http://PgYL6CFz.qwgct.cn
http://NJ3p9USk.qwgct.cn
http://hzzKSgpK.qwgct.cn
http://EyhMzFM4.qwgct.cn
http://2Ms4eFLr.qwgct.cn
http://n5LYwgeb.qwgct.cn
http://zqtl6hf9.qwgct.cn
http://B1NLnNWV.qwgct.cn
http://qirz5KYG.qwgct.cn
http://fXdck1Al.qwgct.cn
http://bCZV7im1.qwgct.cn
http://sVEvN5Fz.qwgct.cn
http://Xyev8M86.qwgct.cn
http://0k630Dsg.qwgct.cn
http://QREn5wUf.qwgct.cn
http://6lsqw7oT.qwgct.cn
http://www.dtcms.com/a/379216.html

相关文章:

  • 如何选择合适的双轴倾角传感器厂家提升水平监测准确性?
  • 洛谷PP5318 查找文献 (深度搜索与广度搜索)详解
  • 手机云服务是什么意思?
  • Linux 基础操作全攻略:从文件解压到服务器管理
  • web:ts的类类型
  • 初识StarRocks
  • linux常见的基础命令及其作用
  • 12 Prompt 模板化与参数化
  • 自动化车间无线安灯呼叫系统解决方案
  • Oracle APEX 如何运行页面时跳过登录页
  • list容器
  • Docker Compose:轻松管理多容器应用
  • 云蝠智能大模型呼叫新模型上线,拥抱AGI
  • 网站SEO内部优化一般包括哪些内容和方法
  • 18j621-3通风天窗图集pdf(免费高清版)
  • 以下是UniApp启动速度优化的深度方案
  • GoogLeNet实战:用PyTorch实现经典Inception模块
  • verilog中task的使用
  • SpringDoc OpenAPI集成spring boot3
  • 安卓13_ROM修改定制化-----安卓 13 系统 ROM 定制化与低版本系统的核心区别
  • yolo学习笔记02——yolo简介
  • OpenCV 开发 -- 图像算术运算
  • 字符串-43.字符串相乘-力扣(LeetCode)
  • java properties/反射基础
  • solidity的高阶语法4
  • Vue.js Data定义方式对比 data() { return {...} } 与 data: {} 的区别
  • P11961原根判断(1)
  • 特征空间的转换方法 IPM/LSS/Transformer
  • 【Vue3】05-Options API和Composition API的区别
  • 锁框架-面试