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

背包问题的分类

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

相关推荐
大P哥阿豪几秒前
Go defer(二):从汇编的角度理解延迟调用的实现
开发语言·汇编·后端·golang
风象南15 分钟前
SpringBoot 与 HTMX:现代 Web 开发的高效组合
java·spring boot·后端
wstcl1 小时前
让你的asp.net网站在调试模式下也能在局域网通过ip访问
后端·tcp/ip·asp.net
ai小鬼头9 小时前
Ollama+OpenWeb最新版0.42+0.3.35一键安装教程,轻松搞定AI模型部署
后端·架构·github
萧曵 丶10 小时前
Rust 所有权系统:深入浅出指南
开发语言·后端·rust
老任与码10 小时前
Spring AI Alibaba(1)——基本使用
java·人工智能·后端·springaialibaba
华子w90892585911 小时前
基于 SpringBoot+VueJS 的农产品研究报告管理系统设计与实现
vue.js·spring boot·后端
星辰离彬11 小时前
Java 与 MySQL 性能优化:Java应用中MySQL慢SQL诊断与优化实战
java·后端·sql·mysql·性能优化
GetcharZp12 小时前
彻底告别数据焦虑!这款开源神器 RustDesk,让你自建一个比向日葵、ToDesk 更安全的远程桌面
后端·rust
jack_yin13 小时前
Telegram DeepSeek Bot 管理平台 发布啦!
后端