news 2026/6/10 14:26:05

动态规划(四)算法设计与分析 国科大

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划(四)算法设计与分析 国科大

0-1背包问题

  • 输入:给定物品集合,每个物品 i 对应重量和价值;同时给定背包的总重量限制 W。
  • 输出:选择物品的一个子集,满足 “子集总重量不超过 W” 的约束,同时最大化子集的总价值。

这是一个二元决策问题,简单来说,需要在一个有限容量的背包中放入尽可能高价值的物品,对于每个可选物品,只有放入\不放入两种状态。

相较于分数背包问题(即非二元决策,如某个物品可以选择放入一半),0-1背包问题无法通过贪心的思想来解决,比如下例:

背包的重量限制为 15kg,待选物品的属性(价值、重量、单位价值)如下:

如果按照贪心的思路,即选择单位价值更高的物品,步骤如下:

  1. 优先选单位价值最高的物品 1(9kg,10$),背包剩余重量:15-9=6kg;
  2. 物品 2(12kg)超过剩余重量,不选;
  3. 选次高单位价值的物品 3(2kg,1$),背包剩余重量:6-2=4kg;
  4. 物品 4(7kg)、物品 5(5kg)均超过剩余重量,不选;

最终选择 “物品 1 + 物品 3”,总价值 11$,总重量 11kg。但若选择 “物品 1 + 物品 5”,总重量为9+5=14kg(不超过限制),总价值为10+2=12美元,明显高于贪心方法得到的 11 美元。这一缺陷的根源是:0-1 背包的 “物品不可拆分” 约束,使得贪心算法无法灵活调整物品组合,因此必须通过动态规划等方法,枚举所有合法组合的价值,才能得到真正的最优解。

寻找最优子结构

我们可以将 0-1 背包的求解过程转化为每个物品是否选择的多阶段决策:每个决策阶段对应一个物品(例如第i阶段决定 “是否选择物品i”),所有阶段的决策组合(选 / 不选的集合),对应最终的物品子集。

以 “是否选择最后一个物品n” 作为首个决策(假设物品按顺序排列,从后往前分析),该决策包含两种互斥选项,每种选项对应一个子问题:

  • 选项 1:选择物品n约束变为 “背包剩余重量为”,需从物品中选择子集,最大化总价值(此时总价值需加上物品n的价值)。
  • 选项 2:不选择物品n约束保持 “背包重量限制为W”,需从物品中选择子集,最大化总价值。

因此,原问题(物品、重量限制)的最优解,可通过 “是否选择物品n” 的决策,转化为两个子问题的最优解的最大值:

下面以物品信息:;背包重量限制展示一下算法过程:

  • 目标:设置 “无物品(i=0)” 时的所有子问题解。
  • 逻辑:当没有物品可选时,任何重量限制下的总价值都是 0,因此OPT[0, w] = 0(w从 0 到 6)。

  • 目标:计算 “前 1 个物品、重量限制的最大价值。
  • 逻辑:第 1 个物品重量,仅当时可选择:取最大值 2,填充OPT[1,2]为 2。

  • 目标:计算 “前 2 个物品、重量限制的最大价值。
  • 逻辑:第 2 个物品重量,需基于前 1 个物品的解计算:取最大值 4,填充OPT[2,4]为 4。

  • 目标:计算 “前 3 个物品、重量限制的最大价值。
  • 逻辑:第 3 个物品重量,需基于前 2 个物品的解计算:取最大值 3,填充OPT[3,3]为 3。

最终可通过OPT[3,6]得到原问题的最优价值。

算法总结如下

用二维数组OPT[i, w]简化表示子问题OPT({1,2,...,i}, w),即前i个物品在重量限制w下的最大价值。之后按状态转移方程来进行即可。

回溯

步骤 1:回溯原问题 OPT[3,6](前 3 个物品、重量 6)

  • 计算逻辑:OPT[3,6] = max(OPT[2,6]=4, OPT[2, 6-3] + v_3=OPT[2,3]+3=2+3=5),最终值为 5。
  • 决策:因OPT[3,6] = OPT[2,3] + v_3,说明选择了物品 3,后续回溯到OPT[2,3](前 2 个物品、重量6-3=3)。

步骤 2:回溯子问题 OPT[2,3](前 2 个物品、重量 3)

  • 计算逻辑:OPT[2,3] = max(OPT[1,3]=2, OPT[1, 3-2] + v_2=OPT[1,1]+2=0+2=2),最终值为 2。
  • 决策:因OPT[2,3] = OPT[1,1] + v_2,说明选择了物品 2,后续回溯到OPT[1,1](前 1 个物品、重量3-2=1)。

步骤 3:回溯子问题 OPT[1,1](前 1 个物品、重量 1)

  • 因物品 1 的重量w_1=2 > 1,无法选择,故OPT[1,1]=0,说明未选择物品 1

洛谷上对应的题目

对应代码如下:

#include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int t, m; cin >> t >> m; vector<int> time(m+1), val(m+1); // 草药1~m for (int i=1; i<=m; i++) { cin >> time[i] >> val[i]; } // 定义opt[前i个物品][时间j],初始化为0 vector<vector<int>> opt(m+1, vector<int>(t+1, 0)); for (int i=1; i<=m; i++) { // 处理前i个物品 for (int j=1; j<=t; j++) { // 处理时间j if (j < time[i]) { opt[i][j] = opt[i-1][j]; // 装不下,不选 } else { // 选/不选的最大值 opt[i][j] = max(opt[i-1][j], opt[i-1][j-time[i]] + val[i]); } } } cout << opt[m][t]; // 前m个物品、时间t的最大价值 return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 0:04:14

面向生产环境的LLM Prompt 优化:从零基础入门到精通,一篇全搞定!

文章介绍了四种提升LLM应用性能的技术&#xff1a;利用缓存token降低成本和延迟&#xff0c;将用户问题置于提示末尾提升回答质量&#xff0c;使用提示优化器改进提示结构&#xff0c;以及建立定制化基准测试选择最适合的模型。这些方法简单易行&#xff0c;能显著提高LLM应用的…

作者头像 李华
网站建设 2026/6/10 13:13:19

网站攻击技术,一篇打包带走!

网站攻击技术&#xff0c;一篇打包带走&#xff01; 大家好&#xff0c;今天给大家介绍一下&#xff0c;Web安全领域常见的一些安全问题。 1. SQL 注入 SQL注入攻击的核心在于让Web服务器执行攻击者期望的SQL语句&#xff0c;以便得到数据库中的感兴趣的数据或对数据库进行读…

作者头像 李华
网站建设 2026/6/10 13:13:40

【量子开发效率翻倍秘诀】:深度集成VS Code实现Q#与Python双向代码导航

第一章&#xff1a;量子开发效率翻倍的核心挑战在当前量子计算快速发展的背景下&#xff0c;提升开发效率成为推动技术落地的关键。然而&#xff0c;受限于硬件稳定性、算法抽象层级低以及工具链不成熟等因素&#xff0c;开发者面临诸多障碍。要实现量子开发效率的翻倍增长&…

作者头像 李华
网站建设 2026/6/10 13:12:18

面向数字孪生系统的全方位测试解决方案

1 测试背景与目标 1.1 背景分析 数字孪生作为物理实体在虚拟空间的动态映射体&#xff0c;其测试复杂度远超传统软件系统。根据Gartner最新研究报告&#xff0c;到2027年超过70%的制造业企业将使用数字孪生技术进行流程优化&#xff0c;这对测试从业者提出了三大核心挑战&…

作者头像 李华
网站建设 2026/6/7 17:11:10

【044】Executors 是陷阱!Executor 实战优化,生产环境不翻车的秘诀

文章目录零、引入一、王二的致命坑&#xff1a;Executors 的 “甜蜜陷阱”二、用 “医院挂号” 讲透 Executor 实战优化&#xff1a;可控才是王道&#x1f4af; Executor 实战优化 4 大核心&#xff08;王二记在病历本上&#xff09;三、实战 1&#xff1a;线程池隔离 批量任务…

作者头像 李华