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

背包问题的分类

今天题目的难点在于如何理解将二维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]]获取到的仍是上一行的值。

相关推荐
bobz9658 分钟前
ebpf 在容器(veth-pair)场景中零 copy 的原理
后端
BingoGo23 分钟前
2025 年 PHP 常见面试题整理以及对应答案和代码示例
后端·php
bobz96530 分钟前
Maglev 哈希在 Cilium 中的实践与优势
后端
RoyLin31 分钟前
TypeScript设计模式:单例模式
前端·后端·node.js
RoyLin34 分钟前
TypeScript设计模式:工厂方法模式
前端·后端·node.js
知其然亦知其所以然34 分钟前
MySQL 社招必考题:如何优化查询过程中的数据访问?
后端·mysql·面试
用户40993225021234 分钟前
FastAPI秒杀库存总变负数?Redis分布式锁能帮你守住底线吗
后端·ai编程·trae
平平无奇的开发仔34 分钟前
# Springboot 中BeanDefinition是在什么阶段被创建成Bean的
后端
掘金酱36 分钟前
🎉 2025年8月金石计划开奖公示
前端·人工智能·后端
SimonKing37 分钟前
接口调用总失败?试试Spring官方重试框架Spring-Retry
java·后端·程序员