01背包学习笔记-二维dp数组问题

参考代码随想录:https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-1.html#%E4%BA%8C%E7%BB%B4dp%E6%95%B0%E7%BB%8401%E8%83%8C%E5%8C%85

一、问题描述

在这里插入图片描述

二、案例讲解

在这里插入图片描述

2.1 确定dp数组以及下标的含义

 对于背包问题,可以使用二维数组进行求解,dp[i][j]代表从下标[0-i]的物品里任意选取,放进容量为j的背包中,价值总和最大是多少?

可以理解为,你现在有一个书包,可以装得下j重量的物品,现在有0-i个物品让你选取(但是每一种物品只有一个),使得最后获取价值总和最大

在这里插入图片描述

2.2 确定递推公式

这里dp[i][j]的状态就是:已经选取i - 1个物品,在选取第i个物品所获得的最大价值是多少,那么该状态是由两种状态推断出,一种是不放物品i(物品i放不进去了),另一种是放物品i(物品i放的进去)

  • 不放物品i(就是物品i放不进去了
    由dp[i - 1][j]推断出,也就是背包的容量是j,里面有i - 1个物品,那么计算此时的最大价值

    通俗的讲,就是物品i放不进去了,那么现在你得书包还有i - 1个物品,此时的最大价值就是dp[i - 1][j],也可以这么想:不放物品i ,前面i - 1个物品占据了容量为j的背包

  • 放物品i(物品i放的进去)

    由dp[i - 1][weight]推断出,dp[i - 1][j - weight[i]]是背包容量为j - weight[i]的时候不放物品I的最大价值(可以这么理解,背包的容量还是j,那么我把物品I放进去之后,原来的i - 1个物品只能占用j - weight[i]的容量了!!!),那么dp[i - 1][j - weight[i]] + value[i](物品I的价值),就是背包放物品i得到的最大价值

    通俗的来说,你有一个书包,书包的容量是j,你现在要把物品I放进去,整个物品集被分为两个部分,前i- 1个物品,和第i个物品,原先i - 1个物品占用的是容量为j的书包,但是现在第i个物品来了之后,原先i - 1个物品只能占用j - weight[i]的容量,并且使其最大化,再加上value[i]就是dpij

递推公式:dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - weight[i]] + value[i])

2.3 初始化dp数组

我们看这个地推公式,可以发现,dp[i][j]是由两部分得出的,其中dp[i - 1][j]从图中上方一个格子得出的,dp[i - 1][j - weight[i]]是从左上方一个格子得出的

在这里插入图片描述

所以,我们初始化的是由一定要先初始化第一行和第一列,不然的话中间那些格子就没办法得出结果。

对于第一列,背包的容量是0,那么什么东西都放不下,第一列价值全部初始化为0,对于第一行,物品0的重量是1,那么所有背包都可以满足物品0,那就全部初始化为物品0的价值15


for(int j =0; j < weight[0]; j++)
{
    dp[0][j] = 0;// 所有元素都初始化为0
}

// 第一行元素初始化
for(int j = weight[0]; j <= bagWeight; j++)
{
    dp[0][j] = value[0];
}

那么,其余那些元素该如何初始化,由于dp[i][j]是由上方元素和左上方元素推到得到的,所以初始化为任何值即可,因为都会被覆盖

那么,最后的初始化dp数组的代码如下:


vector<vector<int>> dp(weight.size(),vector<int>(bagWeight + 1,0));

for(int j = weigth[0]; j <= bagWeight; j++)
{
    dp[0][j] = value[0];
}

2.4 确定遍历顺序

  • 先遍历物品在遍历背包重量:
    // 外层循环遍历物品
    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]]);
          }
      }
    }
    

2.5 完整版的代码


void func()
{
    vector<int> weight = {1,3,4};
    vector<int> value = {15,20,30};
    int bagWeight = 4;// 背包的最大容量

    // 二维数组  先全部初始化为0
    // weight.size() 就是物品的个数
    // 对于每一行元素 全部初始为0 
    vector<vector<int>> dp(weight.size() + 1,vector<int>(bagWeight + 1,0));

    // 初始化  第一行元素初始化为第一个物品的价值
    for(int j = weight[0]; j <= bagWeight; j++)
    {
        dp[0][j] = value[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]);
            }
        }
    }

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

}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
多维0-1背包问题是指在给定容量和一组物品的情况下,如何选择物品使得总重量不超过容量且总价值最大化。在这个问题中,每个物品有多个属性,我们要选择的是哪个属性的物品,以及每个物品在选择属性下的是否放入背包(0表示不放入,1表示放入)。 为了解决这个问题,我们可以使用一个二维数组来表示状态转移方程。假设有n个物品和m个属性,dp[i][j]表示前i个物品在j属性下的最大价值。 具体的穷举算法如下: 1. 初始化一个大小为(n+1)*(m+1)的二维数组dp,并将其所有元素初始化为0。 2. 遍历物品i从1到n,属性j从1到m,依次计算dp[i][j]的值。 3. 对于每个物品i和属性j,有两种情况: - 若第i个物品在属性j下的重量大于背包的容量,则dp[i][j] = dp[i-1][j],即不选择放入背包; - 若第i个物品在属性j下的重量小于等于背包的容量,则dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + 第i个物品的价值),即选择放入或不放入背包取决于哪种方式能获得更大的总价值。 最终,dp[n][m]即为所求的最大总价值。 下面是一个简单的用Matlab实现的代码示例: ```matlab function max_value = knapsack(n, m, weights, values, capacity) dp = zeros(n+1, m+1); for i = 1:n for j = 1:m if weights(i, j) > capacity dp(i+1, j+1) = dp(i, j+1); else dp(i+1, j+1) = max(dp(i, j+1), dp(i, j) + values(i, j)); end end end max_value = dp(n+1, m+1); end % 测试 n = 3; m = 2; weights = [3, 1; 2, 2; 1, 3]; values = [2, 3; 4, 5; 6, 7]; capacity = 5; max_value = knapsack(n, m, weights, values, capacity); disp(max_value); ``` 以上代码中,n表示物品的数量,m表示属性的数量,weights是一个n*m的矩阵,表示每个物品在不同属性下的重量,values是一个n*m的矩阵,表示每个物品在不同属性下的价值,capacity表示背包的容量。请根据实际情况修改测试用例中的n、m、weights、values和capacity的值,以便验证代码的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少写代码少看论文多多睡觉

求打赏,求关注,求点赞

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值