news 2026/1/11 5:08:25

动态规划01背包问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
动态规划01背包问题

动态规划:01背包问题

情景

现在有一个容量有限的背包(比如能装10公斤的东西),现在有价值不同,重量也不同的几件物品,我们要怎样装才能让这个背包尽可能的装的价值最高

这就是为什么这个问题叫01背包问题,每个物品只有两种状态,放入(1)和不放入(0)

问题

有一个背包,最大承重W=10。有4件物品

  • 物品1:重量2,价值3
  • 物品2:重量3,价值4
  • 物品3:重量4,价值5
  • 物品4:重量5,价值6

问题:选择哪些物品放入背包,使总价值最大且总重量≤10?

普通解法
  1. 暴力计算

我们如果想把每个组合都试一遍,总能试出答案

#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;intbruteForce01(vector<int>&values,vector<int>&weights,intcapacity){intn=values.size();intmaxVal=0;for(intmask=0;mask<(1<<n);mask++){intcurWeight=0,curValue=0;for(inti=0;i<n;i++){if(mask&(1<<i)){curWeight+=weights[i];curValue+=values[i];}}if(curWeight<=capacity){maxVal=max(maxVal,curValue);}}returnmaxVal;}intmain(){vector<int>values={3,4,5,6};vector<int>weights={2,3,4,5};intcapacity=10;cout<<"暴力解法结果: "<<bruteForce01(values,weights,capacity)<<endl;return0;}

确实算出了结果,但这个方法非常非常的麻烦,n = 4的情况下用了16种方法才试出答案,时间复杂度时O(n^2)

  1. 递归
#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;intknapsackRecursive(inti,intremaining,vector<int>&values,vector<int>&weights){if(i==values.size())return0;intnotTake=knapsackRecursive(i+1,remaining,values,weights);inttake=0;if(remaining>=weights[i]){take=knapsackRecursive(i+1,remaining-weights[i],values,weights)+values[i];}returnmax(notTake,take);}intmain(){vector<int>values={3,4,5,6};vector<int>weights={2,3,4,5};intcapacity=10;cout<<"递归解法结果: "<<knapsackRecursive(0,capacity,values,weights)<<endl;return0;}

递归解法将这个二问题拆成小问题来解决,将选取这个物品和不选这个物品作比较,取最大值

这个解法看似没有暴力计算这么麻烦,但是在计算的过程中会有重叠子问题,重复的计算还是会带来效率的低下,那么为了避免重叠子问题,我们要使用动态规划

动态规划

二维数组

#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;intknapsackDP(vector<int>&values,vector<int>&weights,intcapacity){intn=values.size();vector<vector<int>>dp(n+1,vector<int>(capacity+1,0));for(inti=1;i<=n;i++){intweight=weights[i-1];intvalue=values[i-1];for(intw=0;w<=capacity;w++){if(w<weight){dp[i][w]=dp[i-1][w];}else{dp[i][w]=max(dp[i-1][w],dp[i-1][w-weight]+value);}}}returndp[n][capacity];}intmain(){vector<int>values={3,4,5,6};vector<int>weights={2,3,4,5};intcapacity=10;cout<<knapsackDP(values,weights,capacity)<<endl;return0;}

我们创建了一个二维的数组,行代表考虑前i个物品,列代表背包当前的容量,两者合起来就是在考虑前i个物品时,背包容量为w时的最大值

现在将它初始化

现在对前k个物品进行外层遍历,在对背包的容量进行内层遍历,对于每个dp[i][w]我们都有两种情况,装的下和装不下,装不下那只能不选了,重在在于这个装的下,在这个时候,我们会有两种选择,装和不装,这时候就要比较不装的时候,和腾出该空间后剩下的价值比较

所以同样是比较,动态规划只需要计算一遍即可,比递归要快的多

一维数组

#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;intknapsack01_1D(vector<int>&values,vector<int>&weights,intcapacity){intn=values.size();vector<int>dp(capacity+1,0);for(inti=0;i<n;i++){for(intw=capacity;w>=weights[i];w--){dp[w]=max(dp[w],dp[w-weights[i]]+values[i]);}}returndp[capacity];}intmain(){vector<int>values={3,4,5,6};vector<int>weights={2,3,4,5};intcapacity=10;cout<<knapsack01_1D(values,weights,capacity)<<endl;return0;}

dp[w]代表背包容量为w时的最大价值,仍然是熟悉的两层遍历,唯一不同的一点就是内层遍历变成了逆序遍历

为什么会这样?

这个数组只有一维,有限的空间使得计算时会包含当前的物品如果我们使用正序遍历,在dp的计算中会出现重复的计算,会存在一个物品被用了两次

所以我们使用逆序,从大容量向小容量思考,在计算dp时保证不含当前物品

解法对比表

比较维度暴力搜索普通递归动态规划(2D)动态规划(1D)
时间复杂度O(2ⁿ)O(2ⁿ)O(n×capacity)O(n×capacity)
是否重复计算大量重复(检查所有组合)大量重复(相同状态多次计算)无重复无重复
计算方向无方向自顶向下自底向上自底向上
代码复杂度简单中等中等中等(需理解逆序)
适用数据规模n≤15n≤20大中规模大规模
核心优势简单直观思维自然标准模板易理解空间最优速度快

总结

学习01背包问题更能理解动态规划的本质,理解其中空间换时间的思想,这篇文章是我之前动态规划的进一步学习,也为学习后边其他背包问题做铺垫

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/10 3:12:31

WinForm DataGridView:单元格类型与高频绘制案例

目录 一、前置准备 二、DataGridView 常用单元格类型&#xff08;基础必掌握&#xff09; 1. 文本框单元格&#xff08;DataGridViewTextBoxColumn&#xff09; 2. 复选框单元格&#xff08;DataGridViewCheckBoxColumn&#xff09; 3. 下拉框单元格&#xff08;DataGridV…

作者头像 李华
网站建设 2025/12/14 19:13:11

java计算机毕业设计社区志愿者服务系统 智慧社区公益志愿协同平台 基层志愿者数字化运营管理系统

计算机毕业设计社区志愿者服务系统38q2o9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。当“志愿红”成为社区里最温暖的底色&#xff0c;传统的人工登记、微信群接龙、纸质工时…

作者头像 李华
网站建设 2025/12/16 20:46:15

考核算法题纠错

考核题算法题纠错 打家劫舍int rob(int* nums, int numsSize) {if (numsSize 0) return 0;if (numsSize 1) return nums[0];int prev_prev nums[0];int prev nums[0] > nums[1] ? nums[0] : nums[1];for (int i 2; i < numsSize; i) {int current prev > (prev…

作者头像 李华
网站建设 2025/12/16 21:11:34

天天劈砖休闲小游戏Linux演示教程

※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※ 本站教程、资源皆在单机环境进行&#xff0c;仅供单机研究学习使用。 ※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※ 一、获取材料和结果演示 百度网盘链接: https://…

作者头像 李华
网站建设 2025/12/30 16:57:53

普中开发板基于51单片机贪吃蛇游戏设计

基于51单片机贪吃蛇游戏设计( proteus仿真程序设计报告讲解视频&#xff09; 仿真图proteus8.17(有低版本) 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;P24 1主要功能&#xff1a; 基于51单片机的贪吃蛇游戏设计 1、采用8*8点…

作者头像 李华
网站建设 2025/12/14 18:43:02

《从零入门 Ascend C:手把手实现高性能向量加法自定义算子》

1. 引言&#xff1a;为什么需要 Ascend C&#xff1f;在深度学习模型训练与推理中&#xff0c;标准算子库&#xff08;如 cuDNN、ACL&#xff09;虽已高度优化&#xff0c;但面对新型网络结构、特殊数据格式或极致性能需求时&#xff0c;往往力不从心。此时&#xff0c;开发者需…

作者头像 李华