目录
对于学计算机的同学来说,学习算法是一件非常重要的事情,废话不多讲,我们来讲讲“01背包问题”。
何谓01背包,对于每个要放入背包的物品,只需要考虑放与不放两种选择。如果不选择将其放入背包中,则不需要处理,视为0,反之视为1。
有N件物品和一个容量为V的背包。放入第 i 件物品耗费的费用(体积)是 Ci,得到的价值是 Wi。求:将哪些物品装入背包可使价值总和最大。
01背包是最基础的背包问题,特点是:每种物品只有一件,可以选择放或者不放。
我们第一反应要想到用子问题来定义状态:即 dp[i][v] 表示前 i 件物品放入一个容量为 v 的背包中可以获得的最大价值。其状态转移方程为:
dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c[i]] + w[i]);
该方程理解起来非常简单,来解释一下,实际上直接看方程也能大致知道想表达什么。
“将前 i 件物品放入容量为 v 的背包中”这个子问题,若只考虑第 i 件物品的策略(放或不放),那么就可以转化为一个只和前 i - 1 件物品相关的问题。如果不放第 i 件物品,那么问题就转化为“前 i - 1 件物品放入容量为 v 的背包中”,价值为 dp[i- 1][v];如果放第 i 件物品,那么问题就转化为“前 i − 1 件物品放入剩下的容量为 v − Ci 的背包中”,此时能获得的最大价值就是 dp[i − 1][v − Ci] 再加上通过放入第 i 件物品获得的价值 Wi。
据说这个方程非常重要,基本上所有跟背包相关的问题的方程都可以由它衍生出来。
最后稍微思考一下可得到递推式逻辑:
(不能装)如果 当前背包容量V[i] < 待装物品体积C[i] ,则 dp[i][v]=dp[i-1][v]
(能装,决策装不装)否则 dp[i][v] = max(dp[i - 1][v], dp[i - 1][v - c[i]] + w[i])
补充:动态规划法与分治法的区别
动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案记录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。
本文着重让读者明白何谓01背包以及解01背包的基本算法思路,将空间二维优化为空间一维需要更好的基础,写了可能读者在比赛或机考时不一定能用得上,综合考虑后决定还是不写,感兴趣的兄弟可以找其他博客阅读阅读,或者到下面例题的题解里面搜寻想要的解释。
这是一道模板题,直接套模板即可。
- #include
- using namespace std;
-
- const int MAXN=1003;
- int V[MAXN]; //每个背包的体积
- int W[MAXN]; //每个背包的价值
- int dp[MAXN][MAXN]; //dp[i][j], j体积下前i个物品的最大价值
-
- int main() {
- int n,v;
- cin>>n>>v;
-
- for(int i=1;i<=n;++i)
- cin>>V[i]>>W[i];
-
- for(int i=1;i<=n;++i){ //该层循环为了遍历每个背包
- for(int j=1;j<=v;++j){ //为了填表中的每一行,得到在背包任意体积下的最优解,填表见下图
- if(j
- dp[i][j]=dp[i-1][j];
- else
- dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]);
- }
- }
-
- cout<
- return 0;
- }
- /* 补充:
- * 当前的状态依赖于之前的状态,我们可以默认为从初始状态(边界) dp[0][j] = dp[i][0] = 0开始决策,
- * 有 N 件物品,则需要 N 次决策,每一次对第 i 件物品的决策,状态dp[i][j]不断由之前的状态更新而来。
- * 显然,当只有 0 件物品的时候,或者背包最大容量为 0 的时候, dp[0][j] = dp[i][0] = 0
- * */
(2)python——二维朴素写法
- MAXN=1003
- dp=[[0]*MAXN for i in range(MAXN+1)] #注意创建二维数组是这样子
- V=[0]*MAXN
- W=[0]*MAXN
-
- # test=[[0]*MAXN]*MAXN #这样创数组有错误
- # print(test[10][10])
-
- n,v=map(int,input().split())
-
- for i in range(1,n+1):
- V[i],W[i]=map(int,input().split())
-
- for i in range(1,n+1):
- for j in range(1,v+1):
- if j
- dp[i][j]=dp[i-1][j]
- else:
- dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i])
-
- print(dp[n][v])
(3)python——一维空间优化写法
- MAXN=1003
- dp=[0]*MAXN
- V=[0]*MAXN
- W=[0]*MAXN
-
- n,v=map(int,input().split())
-
- for i in range(1,n+1):
- V[i],W[i]=map(int,input().split())
-
- for i in range(1,n+1):
- for j in range(v,0,-1):
- if j
- dp[j]=dp[j]
- else:
- dp[j]=max(dp[j],dp[j-V[i]]+W[i])
-
- print(dp[v])
-
- # dp=[0,1,2,3,4,5,6,7,8,9,10,11,21]
- # for i in range(12,-1,-1): #这样是逆序输出全部元素
- # print(dp[i],end=" ")
一维为何要这样写呢?先上一张图。

懂的人自然懂,不懂的人仔细思考一下也会懂~
以上,01背包,更多内容可到Acwing讨论区学习。
祝好,加油!
-
相关阅读:
SMALE实验室论文成果:多标签学习CSDN源码
[JS Framework] 当前运行的基座不包含原生插件[XXX],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座
系统方向学习9--android 10.0 去掉未知来源弹窗 默认授予安装未知来源权限
Win11一些问题以及解决方案
Cargo 教程
【小巧玲珑】文件太大,怎么办?分卷压缩技术了解下,这才是压缩技术
.NET C#杂谈(1):变体 - 协变、逆变与不变
【信息检索与数据挖掘】期末笔记(一)
python np.dot()、np.multiply()、np.matmul()、@、*的用法与区别
python基于django学生成绩管理系统o8mkp
-
原文地址:https://blog.csdn.net/m0_52711790/article/details/127102089
-
最新文章
-
沪漂五周年了:我越来越迷茫了
Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
MySQL-Seconds_behind_master的精度误差
[MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
Agent OS :五种驯服不确定性的范式
PortSwigger SQL注入LAB11
数据库即时编译JIT
[Begin]AI Learn Data Day 0
深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU