01背包问题 | 动态规划 | 学习笔记

01背包问题

有 n 件物品,每件物品重量为 w[i] ,价值为 value[i] 。现有一个容量为V的背包,问如何选取物品放入背包,使得背包物品的总价值最大。
每件物品仅有1件。
样例:
n=5,V=8;
3 5 1 2 2 //w[i]
4 5 2 1 3 //value[i]

解题详情

背包问题背包问题简述

  • 选择策略
    开辟一个长度为V+1的数组 d p dp dp,下标i代表背包容量,数组存储的数值是所能装的🔍最大价值
    i件物品恰好放进容量为a的背包中,可以获得的最大价值。有两种情况,
    1️⃣一种是不放进去的历史i-1状态;
    2️⃣一种是把这个物品放进去之后,即价值增加后的状态。
    保留总价值更大的那个状态
    由此获得状态转移方程

    • 状态转移方程
      d p [ a ] = m a x { d p [ a ] , d p [ a − w [ i ] ] + v a l u e [ i ] } 其中 ( 1 ≤ i ≤ n , w [ i ] ≤ a ≤ V ) dp[a]=max\{ dp[a] , dp[a-w[i]]+value[i] \} \\其中(1\leq i \leq n , w[i] \leq a \leq V) dp[a]=max{dp[a],dp[aw[i]]+value[i]}其中(1in,w[i]aV)
      ⭕️从限制条件看,i控制物品放入,a控制背包容量的大小。
      max()的第一个值dp[a]代表第1️⃣种不放入当前第i个物体的情况。
         ⟺    \iff 之前的历史状态。
      第二个值dp[a-w[i]]+value[i]代表第2️⃣种放入当前第i个物体的情况
         ⟺    \iff 退化到背包容量为a-w[i]的状态,然后该背包的价值基础上,加上该物品的价值value[i]
      👻如果只开辟了一维数组 d p dp dp空间,每一次进行选择时,更新 d p dp dp一定要【倒序】。
  • 🌰利用样例进行解释。

    前3轮最大价值的dp数组变化请添加图片描述

    第0️⃣轮:dp[]数组原始全为0。
    第1个物品的重量为w[0]=3,其价值为value[0]=4
    那么背包容量a>=3的背包都可以放下这个物品,将a>=3的背包总价值记录下来【数组的value】
    第1️⃣轮:dp数组上一轮的结束状态是{0,0,0,4,4,4,4,4,4}
    第2个物品的重量为w[1]=5,其价值为value[1]=5
    背包容量a>=5都放得下该物品。
    此时就要进行选择策略,需要使用到状态转移方程max(dp[a], dp[a-w[i]]+value[i])
    倒序更新a=8的背包,👎 【不放入】历史状态为dp[8]=4,👍 【放入】背包退化成dp[a-w[0]]=dp[8-5]=dp[3]的状态(也就是背包容量减去当前物体的重量,以保证我放入当前物品时,整个背包的最终重量weight=8),此时总价值为dp[8]=dp[3]+value[1]=4+5=9
    ✌️max(4, 9)显然选择【放入】,把当前物体装进去可以使a=8的背包总价值更大
    a=7的背包,👎 【不放入】历史状态为dp[7]=4,👍【放入】背包退化dp[7-5]=dp[2]=0,请注意这里的dp[2]上一轮结束后的背包状态值。放入当前物品后,总价值为dp[7]=dp[2]+value[1]=0+5=5
    ✌️max(4, 5)选择【放入】。也就是说,当前a=7的背包总价值最大,只有把之前放入的物品扔掉,换成当前物品,价值更大。
    剩下的以此类推即可~

样例代码

通过下列代码,你可以只管的看到每一轮最大价值数组 d p dp dp的状态是什么样的。以此验证背包问题的解决。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
    int n=5, V=8;
    int w[5], c[5], dp[9];
    for (int i = 0; i < n; i++) {
        cin >> w[i];
    }
    for (int i = 0; i < n; i++) {
        cin >> c[i];
    }
    for (int v = 0; v <= V; v++) {//初始化dp数组
        dp[v] = 0;
    }
    for (int i = 0; i < n; i++) {
        cout << "当前w[i] = " << w[i] << endl;
        for (int v = V; v >= w[i]; v--) {
            dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
            cout << "重量为 v= " << v << "的物体 -->dp[v] =" << dp[v] << endl;
        }
    }
    int max = 0;
    for (int v = 0; v <= V; v++) {
        if (dp[v] > max) {
            max = dp[v];
        }
    }
    cout << "max = " << max << endl;
    return 0;
}

所有参考自胡凡的《算法笔记》。动画纯自制,本篇仅作为自己学习笔记,并无抄袭之意。有任何问题可以评论聊~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值