• 01背包问题


    目录

    一、前言

    二、01背包

    1、问题描述

    2、基本思路

    3、优化空间复杂度

    三、题例

    1、上链接

    2、题目分析

    3、代码

    (1)C++

    (2)python——二维朴素写法

    (3)python——一维空间优化写法


    一、前言

    对于学计算机的同学来说,学习算法是一件非常重要的事情,废话不多讲,我们来讲讲“01背包问题”。

    二、01背包

    何谓01背包,对于每个要放入背包的物品,只需要考虑放与不放两种选择。如果不选择将其放入背包中,则不需要处理,视为0,反之视为1。

    1、问题描述

    有N件物品和一个容量为V的背包。放入第 i 件物品耗费的费用(体积)是 Ci,得到的价值是 Wi。求:将哪些物品装入背包可使价值总和最大。

    2、基本思路

    ​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])

    补充:动态规划法与分治法的区别

    动态规划与分治法类似,都是把大问题拆分成小问题,通过寻找大问题与小问题的递推关系,解决一个个小问题,最终达到解决原问题的效果。但不同的是,分治法在子问题和子子问题等上被重复计算了很多次,而动态规划则具有记忆性,通过填写表把所有已经解决的子问题答案记录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间,所以在问题满足最优性原理之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。

    3、优化空间复杂度

    本文着重让读者明白何谓01背包以及解01背包的基本算法思路,将空间二维优化为空间一维需要更好的基础,写了可能读者在比赛或机考时不一定能用得上,综合考虑后决定还是不写,感兴趣的兄弟可以找其他博客阅读阅读,或者到下面例题的题解里面搜寻想要的解释。

    三、题例

    1、上链接

    2. 01背包问题 - AcWing题库

    2、题目分析

    这是一道模板题,直接套模板即可。

    3、代码

    (1)C++

    1. #include
    2. using namespace std;
    3. const int MAXN=1003;
    4. int V[MAXN]; //每个背包的体积
    5. int W[MAXN]; //每个背包的价值
    6. int dp[MAXN][MAXN]; //dp[i][j], j体积下前i个物品的最大价值
    7. int main() {
    8. int n,v;
    9. cin>>n>>v;
    10. for(int i=1;i<=n;++i)
    11. cin>>V[i]>>W[i];
    12. for(int i=1;i<=n;++i){ //该层循环为了遍历每个背包
    13. for(int j=1;j<=v;++j){ //为了填表中的每一行,得到在背包任意体积下的最优解,填表见下图
    14. if(j
    15. dp[i][j]=dp[i-1][j];
    16. else
    17. dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i]);
    18. }
    19. }
    20. cout<
    21. return 0;
    22. }
    23. /* 补充:
    24. * 当前的状态依赖于之前的状态,我们可以默认为从初始状态(边界) dp[0][j] = dp[i][0] = 0开始决策,
    25. * 有 N 件物品,则需要 N 次决策,每一次对第 i 件物品的决策,状态dp[i][j]不断由之前的状态更新而来。
    26. * 显然,当只有 0 件物品的时候,或者背包最大容量为 0 的时候, dp[0][j] = dp[i][0] = 0
    27. * */

    (2)python——二维朴素写法

    1. MAXN=1003
    2. dp=[[0]*MAXN for i in range(MAXN+1)] #注意创建二维数组是这样子
    3. V=[0]*MAXN
    4. W=[0]*MAXN
    5. # test=[[0]*MAXN]*MAXN #这样创数组有错误
    6. # print(test[10][10])
    7. n,v=map(int,input().split())
    8. for i in range(1,n+1):
    9. V[i],W[i]=map(int,input().split())
    10. for i in range(1,n+1):
    11. for j in range(1,v+1):
    12. if j
    13. dp[i][j]=dp[i-1][j]
    14. else:
    15. dp[i][j]=max(dp[i-1][j],dp[i-1][j-V[i]]+W[i])
    16. print(dp[n][v])

    (3)python——一维空间优化写法

    1. MAXN=1003
    2. dp=[0]*MAXN
    3. V=[0]*MAXN
    4. W=[0]*MAXN
    5. n,v=map(int,input().split())
    6. for i in range(1,n+1):
    7. V[i],W[i]=map(int,input().split())
    8. for i in range(1,n+1):
    9. for j in range(v,0,-1):
    10. if j
    11. dp[j]=dp[j]
    12. else:
    13. dp[j]=max(dp[j],dp[j-V[i]]+W[i])
    14. print(dp[v])
    15. # dp=[0,1,2,3,4,5,6,7,8,9,10,11,21]
    16. # for i in range(12,-1,-1): #这样是逆序输出全部元素
    17. # 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