背包问题 | 豆包MarsCode AI刷题

191 阅读3分钟

背包问题是动态规划中一类相当经典的问题 ,下面 ,我将介绍我在使用豆包MarsCode AI刷题的过程中经验总结

这里给一道例题 : 0,1背包最大价值问题 - MarsCode

01背包问题

由题易得,
制约答案的条件有两个,前i个元素(数目限制)
体积限制 —> 二维状态 状态f[i][j]定义:前 i 个物品,背包容量 j下的最优解(最大价值)
通过对i个元素分析讨论有两种情况
当前背包容量够,可以选,因此需要决策选与不选第 i个物品:
选:f[i][j] = f[i - 1][j - v[i]] + w[i]。
不选:f[i][j] = f[i - 1][j] 。
我们的决策是如何取到最大价值,因此以上两种情况取 max()
所以f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])

1833_e192f0d8d6-Snipaste_2019-09-13_17-06-58.png

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;
int n,m;
int v[N],w[N];
int f[N][N];


int main()
{
    cin >> n >>m;
    for (int i = 1; i <=n; i ++ )
    {
        cin >> v[i] >> w[i];
    }
    for (int i = 1; i <=n; i ++ )
    {
        for (int j = 1; j <=m; j ++ )
          {
              f[i][j] = f[i-1][j];
              if(j>=v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
          }
    }
    cout << f[n][m]<< endl;
 return 0;   
}

优化版

分析得,由于在i的循环中,只涉及i和i-1的相对关系,可以用滚动数组
即数组的更新前和更新后可以表示两种状态

cb5e6d769235f0fffbea49590fd0607.png


#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;
int n,m;
int v[N],w[N];
int f[N];


int main()
{
    cin >> n >>m;
    for (int i = 1; i <=n; i ++ )
    {
        cin >> v[i] >> w[i];
    }
     for (int i = 1; i <=n; i ++ )
    {
      for (int j =m; j>=v[i]; j -- ) 
          {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
          }
    }
    cout << f[m]<< endl;
 return 0;   
}

将状态f[i][j]优化到一维f[j],实际上只需要做一个等价变形
状态转移方程为:f[j] = max(f[j], f[j - v[i]] + w[i]

ABAP复制代码

f[j] = max(f[j],f[j-v[i]]+w[i]);与f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i])
对比可知: 在新一轮的滚动过程中需要要要到的是上一轮的f[i]
(即此轮的f[i-1]),所以在更新f的时候需要倒序遍历才能保证是上一轮的f[i]

遍历顺序的理解

1先遍历背包还是物品 , 在求最大价值时并没有明显的区别 , 按照习惯先遍历物品即可

2当在别的场景下 , 就要自己根据题目进行判题了
参考视频 : 装满背包有多少种方法?518.零钱兑换II ,
先遍历物品求组合数
理解 : 先遍历物品时相当于固定了某一个物品 , 枚举所有的背包容量 , 在枚举第 i 个物品后 , 必定枚举 > i 的物品
所以物品的填放按照 i 从小到大的顺序 , 有序的枚举方案数且不重复即为组合数
先遍历背包容量求组合数
理解 : 先遍历容量则在每一容量下 for 循环可以任意的选择物品 , 物品的枚举出来的顺序并不一定有序 , 则枚举的元素有可能元素相同 ,而顺序不同 , 为排列数