【动态规划】详解背包问题之二维数组转一维

背包问题的分类

今天题目的难点在于如何理解将二维dp数组转换成一维的dp数组,我们先看下题目:

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

输入描述

第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。

第二行包含 M 个正整数,代表每种研究材料的所占空间。

第三行包含 M 个正整数,代表每种研究材料的价值。

输出描述

输出一个整数,代表小明能够携带的研究材料的最大价值。

输入示例

复制代码
6 1
2 2 3 1 5 2
2 3 1 5 4 3

输出示例

复制代码
5

提示信息

小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。

数据范围:

1 <= N <= 1000

1 <= M <= 1000

研究材料占用空间和价值都小于等于 1000

按照卡哥的动规五部曲思路我们进行如下分析(感谢卡哥)

  • 第一步:确定dp[i][j]的具体含义:表示从下标为[0 - i-1]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
  • 第二步:初始化 dp 数组做了简化(给物品增加冗余维)。这样初始化dp数组,默认全为0即可。这一步其实就是模仿背包重量从 0 开始,背包容量 j 为 0 的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为 0。可选物品也可以从无开始,也就是没有物品可选,即dp[0][j],这样无论背包容量为多少,背包价值总和一定为 0。
  • 第三步:确定递推公式
    • 当前背包的容量都没有当前物品i大的时候,是不放物品i的,那么前i-1个物品能放下的最大价值就是当前情况的最大价值
    • 当前背包的容量可以放下物品i,那么此时分两种情况:
      • 1、不放物品i
      • 2、放物品i
    • 比较这两种情况下,哪种背包中物品的最大价值最大
  • 第四步:确定遍历顺序
  • 第五步:打印dp数组
java 复制代码
/**
 * @param weight  物品的重量
 * @param value   物品的价值
 * @param bagSize 背包的容量
 */
public static void testWeightBagProblem(int[] weight, int[] value, int bagSize) {

    // 创建dp数组
    int goods = weight.length;  // 获取物品的数量
    int[][] dp = new int[goods + 1][bagSize + 1];  // 给物品增加冗余维,i = 0 表示没有物品可选

    // 初始化dp数组,默认全为0即可
    // 填充dp数组
    for (int i = 1; i <= goods; i++) {
        for (int j = 1; j <= bagSize; j++) {
            if (j < weight[i - 1]) {  // i - 1 对应物品 i
                dp[i][j] = dp[i - 1][j];
            } else {
                /**
                 * 
                 */
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i - 1]] + value[i - 1]);  // i - 1 对应物品 i
            }
        }
    }

    // 打印dp数组
    for (int[] arr : dp) {
        System.out.println(Arrays.toString(arr));
    }
}

那么如何将二维数组优化成一维的? 我们看下代码:

java 复制代码
private static void testWeightBagProblem_3(int[] weight, int[] value, int bagSize) {
    int[] dp = new int[bagSize + 1];
    dp[0] = 0;
    for (int i = 1; i <= bagSize; i++) {
        dp[i] = value[0];
    }
    for (int j = 1; j < value.length; j++) {
        for (int i = bagSize; i >= weight[j]; i--) {
            dp[i] = Math.max(dp[i], dp[i - weight[j]] + value[j]);
        }
    }

}

注意到了吗?跟二维的有什么区别?

倒叙遍历! 为什么要倒叙遍历?

由于一位数组是根据二维优化而来的,因此如果仍然从前向后遍历,遍历到后面的时候前面的元素可能已经被修改过了,此时dp[i - weight[j]]再获取到的就不是上一行的值了,因此需要倒叙遍历,以确保dp[i - weight[j]]获取到的仍是上一行的值。

相关推荐
一点程序7 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
怪兽源码9 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
csdn_aspnet9 小时前
ASP.NET Core 中的依赖注入
后端·asp.net·di·.net core
昊坤说不出的梦10 小时前
【实战】监控上下文切换及其优化方案
java·后端
疯狂踩坑人10 小时前
【Python版 2026 从零学Langchain 1.x】(二)结构化输出和工具调用
后端·python·langchain
橘子师兄12 小时前
C++AI大模型接入SDK—ChatSDK封装
开发语言·c++·人工智能·后端
@ chen12 小时前
Spring事务 核心知识
java·后端·spring
一点技术13 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
RANCE_atttackkk14 小时前
Springboot+langchain4j的RAG检索增强生成
java·开发语言·spring boot·后端·spring·ai·ai编程
好好研究15 小时前
Spring Boot - Thymeleaf模板引擎
java·spring boot·后端·thymeleaf