• 01背包(一) 01背包(二)动态规划


    01背包(一) 二维数组

     题目

    背包最大重量为4。

    物品为:

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

    问背包能背的物品最大价值是多少?

    创建二维数组,dp[i][j]的含义是任意放入前 i 个物品放进在背包重量为j的时候的最大价值

     递推公式

    dp[i][j] = max( dp[i - 1][j] ,  dp[i - 1][j - weight[i]] + value[i] );

    dp[i][j]任意放入前 i 个物品 放进 j容量背包的最大价值) 可以由 dp[i - 1][j]任意放入前 i -1个物品不放物品 i最大价值)和

    dp[i - 1][j - weight[i]] + value[i] 任意放入前 i-1 个物品 同时 放物品 i 的最大价值)推出

     初始化

    初始化 dp[i][0](任意放入前 i 个物品,同时背包容量为 0 的最大价值),也就是价值 “0” 的那一列。

    容量为0,放不了物品,价值为0

    初始化 dp[0][j](放入物品0,背包容量为 j 的最大价值),也就是 价值“15”的那四个格子。

    1. //初始化 dp[i][0](放入物品i,背包容量为 0 的最大价值)
    2. for (int j = weight[0]; j <= bagweight; j++) {
    3. dp[0][j] = value[0];
    4. }
    5. //初始化 dp[0][j](放入物品0,背包容量为 j 的最大价值)
    6. for (int j = 0 ; j < weight[0]; j++) {
    7. dp[0][j] = 0;
    8. }

     后面变成,先全赋值为0,省去背包容量为0的初始赋值,然后赋放入物品0的值

    1. // 初始化 dp
    2. vectorint>> dp(weight.size(), vector<int>(bagweight + 1, 0));
    3. for (int j = weight[0]; j <= bagweight; j++) {
    4. dp[0][j] = value[0];
    5. }

     

    总代码

    先遍历物品再遍历背包容量

    1. void test_2_wei_bag_problem1() {
    2. vector<int> weight = {1, 3, 4};
    3. vector<int> value = {15, 20, 30};
    4. int bagweight = 4;
    5. // 二维数组
    6. vectorint>> dp(weight.size(), vector<int>(bagweight + 1, 0));
    7. // 初始化
    8. for (int j = weight[0]; j <= bagweight; j++) {
    9. dp[0][j] = value[0];
    10. }
    11. // weight数组的大小 就是物品个数
    12. for(int i = 1; i < weight.size(); i++) { // 遍历物品
    13. for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    14. if (j < weight[i]) dp[i][j] = dp[i - 1][j];//如果背包容量不够-不加
    15. else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
    16. //如果容量够,看看加上价值大还是不加上价值大,因为背包是有容量的
    17. }
    18. }
    19. //这个就是右下角,遍历完成的最大价值,因为有0占格子所以-1
    20. cout << dp[weight.size() - 1][bagweight] << endl;
    21. }
    22. int main() {
    23. test_2_wei_bag_problem1();
    24. }

     

    想法:这也是遍历了每一种 背包容量 和 物品和不放物品 的所有情况了吧,每一种情况都判断出最大价值,由上一次的最大价值推出下一次的最大价值,末尾得出答案。

     02背包(二) 一维数组

    可以从二维简化为一维的数组,理由是可以第一行初始化了以后,后面的行可以重复覆盖第一行来得到最后的结果,dp[i][j]只依靠他的左边和正上方推出,变成一维数组不断覆盖后也不影响最终结果的推导(原来二维数组的最右下角是结果)

    题目同(一)

    递推公式

    dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。

    dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    dp[j] 等于 没放入i的最大价值,dp[j - weight[i]] + value[i] 等于 放入i的最大价值

    怎么得出的?

    原来二维数组的递推公式 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i],在递推过程中(左到右上到下),遍历物品1的时候,会用物品1其上方,遍历物品0的值,来计算遍历到的这个格子的值。

    如果采取覆盖的方式,把二维数组变成一维数组,利用正上方的值变成利用本格的值,利用完后再替换成计算出的结果,就可以只用一行完成多行的计算了。

    比如dp[i - 1]变成dp[i]就是这个意思,dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]

    然后可以再去除i,只留下这样的话就去掉了 i 的维度了,但是还是用到i的,我的理解这是一种简化的写法。然后变成

    dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    初始化

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

     

    遍历顺序

    dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    从后往前计算 为什么?

    正序遍历

    dp[1] = dp[1 - weight[0]] + value[0] = 15(这个时候dp[1]=15)

    dp[2] = dp[2 - weight[0]] + value[0] = 30

    此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。

    为什么倒序遍历,就可以保证物品只放入一次呢?

    倒序就是先算dp[2]

    倒序遍历

    dp[2] = dp[2 - weight[0]] + value[0] = 15 (数组已经都初始化为0,就是意思这个时候dp[1]=0)

    dp[1] = dp[1 - weight[0]] + value[0] = 15

    我的理解就是,一维正序遍历中,正在遍历的值会使用前面的值,比如dp[2]使用dp[1]的,一个dp[1]=15,一个等于0,因为是一个是后序初始化为0还没有计算成15,一个是正序先计算了成15了,但不就是要使用前面的值吗?为什么不使用?

    原因是正序计算出的15,覆盖后的值而不是覆盖前的值,而dp[2]需要的dp[1]是覆盖前的值。

    换成二维数组和他的递归公式dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i],就是,覆盖前的值才是上一列前面的值,覆盖后就是下一列前面的值了。

    这也是为啥二维数组正序倒序都可以,而一维数组只能倒序的原因。

    1. for(int i = 0; i < weight.size(); i++) { // 遍历物品
    2. for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
    3. dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    4. }
    5. }

     

    总代码

    1. void test_1_wei_bag_problem() {
    2. vector<int> weight = {1, 3, 4};
    3. vector<int> value = {15, 20, 30};
    4. int bagWeight = 4;
    5. // 初始化
    6. vector<int> dp(bagWeight + 1, 0);
    7. for(int i = 0; i < weight.size(); i++) { // 遍历物品
    8. for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
    9. dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    10. }
    11. }
    12. cout << dp[bagWeight] << endl;
    13. }
    14. int main() {
    15. test_1_wei_bag_problem();
    16. }

    着实是有些抽象的。。 

  • 相关阅读:
    【基础知识】MPP架构和hadoop架构比对
    学习一下 常说的防抖
    使用Vite快速构建Vue3+ts+pinia脚手架
    java springboot电子产品销售网站的设计与实现源码
    Vue2项目知识点总结-尚品汇
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第10章 Vuex状态管理 10.1 Vuex基础应用
    ESP32上实现面向对象的C++ OOP——面向对象的点灯
    vue3移动端嵌入pdf的两种办法
    vue 祖先组件操作后代组件方法
    【软考】设计模式之组合模式
  • 原文地址:https://blog.csdn.net/qq_70280303/article/details/133825691