动态规划之01背包问题思路详细讲解

本文分享了一名大一学生对01背包问题的理解,通过动态规划的五个步骤,详细讲解了如何确定递推公式、初始化dp数组、遍历顺序,并给出了一个示例代码。最后强调了动态规划解决问题的关键在于找到合适的递推关系和初始值。
摘要由CSDN通过智能技术生成

作为一名大一学生,在近期了解完01背包问题后,深感01背包理解之艰,故作此博客与诸君皆看完视频头疼者分享学习心得,望与诸君共勉。

01背包:有n种重量或价值不同的物体,此时有每种物体1个。此时用一个容量为m的背包去装这些

物体,问能装到的最大价值为多少?

首先给一组样例:

重量价值
物品0115
物品1320
物品2430

下方weight【i】value【i】为物体i的重量与价值

按照动态规划的五部曲,同时结合样例,来开始我们的分析吧

1、DP数组的含义

dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少

i,j,以及dp[i][j]所存的值便包含了每个物体,背包容量,而物体的重量与价值也间接的与j,dp[i][j]

挂钩

要时刻记着这个dp数组的含义,下面的一些步骤都围绕这dp数组的含义进行的

2.确定递推公式(最重要的一步)

先大概做个二维数组看下:

由dp[i][j]的定义中“下标为[0到i]的物品”易得dp[2][4]便是我们要求的最终值,显然不可能一步得到我们梦寐以求的最大值,所以我们需要从考虑用某种逻辑在其他方面来变相的得到该值

以本例的最终值dp[2][4]为例,首先以物品二的上面几行为基准,他们表示在下标为【0到1】的物品中对应不同j值是取得的最大值,但本体最终是要扩充到下标为【0到2】的物品中去,所以我们可以得到如下表达式:(因为dp数组中i的定义为[0到i],并不是说第i行就必须会被选中,所以此时物体2便有两种状态)

     不放物品2

  • dp[2][4]由dp[1][4]推出,即背包容量为4,里面不放物品2的最大价值,此时dp[2][4]就是dp[1][4]。其实就是a.当物品i的重量大于背包j的重量b.单纯不装(后面代码中会有相应对应点)时,物品i无法放进背包中,所以背包内的价值依然和前面相同。
  • 放物品2
  • 由dp[1][0]]推出,dp[1][0]+value[2]为背包容量为0的时侯放物品2的最大价值,那么dp[1][0]]就是背包放物品2得到的价值
  • 同理,上面得到的dp[1][4],dp[1][0]也可以由相同的逻辑由其上一行推出直至不可推

因为求背包值的最大值,所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

同时我们也不难得出一个浅显的结论,每一个被推出的值都是由其上一行正上方或左上方得到的

3.确定dp数组的初始值以及遍历 顺序(第二重要的事情)

首先搞清楚为什么要设置初值,因为一直向上递推总要推至不可再推才能中止得值

由dp数组的定义可得到第一行的每个值,很好理解,第一列也可视为放不下物品i是由递推式dp[i - 1][j]得到,因为背包容量为0真的装不下

遍历顺序:因为由上述分析得下一行总由上一行得出,所以我们一行一行来递推,直到最后一行,得到最后一格对应得最终值

for(int i = 1; i < weight.size(); i++) { // 遍历物品
    for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
        if (j < weight[i]) dp[i][j] = dp[i - 1][j];
        else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

    }
}

因为第一行已经初始化,所以从第一行开始遍历

其中第二个for循环下的条件便是我们上述所提再顺带提一嘴为什么每一行得每个j值对应得方格值都要得到,是因为每一行得下一行中不会预先得知weight【i】的值,这也导致dp[i - 1][j - weight[i]]中j - weight[i]未知,所以每一行得每个j值对应得方格值都要得到

4、最终代码

void test_2_wei_bag_problem1() {
    vector<int> weight = {1, 3, 4};
    vector<int> value = {15, 20, 30};
    int bagweight = 4;

    // 二维数组
    vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));

    // 初始化
    for (int j = weight[0]; j <= bagweight; j++) {
        dp[0][j] = value[0];
    }

    // weight数组的大小 就是物品个数
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
            if (j < weight[i]) dp[i][j] = dp[i - 1][j];
            else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

        }
    }

    cout << dp[weight.size() - 1][bagweight] << endl;
}

int main() {
    test_2_wei_bag_problem1();
}

最后解释的一点:因为每一步递推的上一步已经求得了最大值,所以最后一定为最大值板上钉钉

背包最大重量为4。

一些本人觉得还不错的视频讲解网址:0.51 P@X.mQ Oxf:/ 01/07 动态规划中的经典01背包问题你清楚嘛?搞懂它再刷题! # 前端开发工程师# JavaScript# 编程# 程序员# web前端# 前端# 前端开发 https://v.douyin.com/iFPS3q61/ 复制此链接,打开Dou音搜索,直接观看视频!

看完觉得有用点个赞吧,也希望本人能早日获得圆圆的芳心

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值