0,1背包最大价值问题 | 豆包MarsCode AI刷题

214 阅读5分钟
public static int solution(int n, int[] weights, int[] values, int m) {
    // 定义 dp 数组
    int[][] dp = new int[n + 1][m + 1];

    // 遍历每个物品
    for (int i = 1; i <= n; i++) {
        int weight = weights[i - 1];
        int value = values[i - 1];
        // 遍历每个容量
        for (int j = 0; j <= m; j++) {
            // 如果不选当前物品
            dp[i][j] = dp[i - 1][j];
            // 如果选择当前物品
            if (j >= weight) {
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value);
            }
        }
    }

    return dp[n][m];
}

这段代码实现了一个经典的动态规划算法,用于解决0-1背包问题。下面是对代码的逐行解释:

背包问题概述

在传统的0-1背包问题中,有一组物品,每种物品都有一个重量和一个价值。在当前的背包容量允许的情况下,目标是选择物品以便最大化放入背包的总价值。每种物品只能选择一次(即要么选择放入,要么不放)。

代码解析

public static int solution(int n, int[] weights, int[] values, int m) {
  • n: 物品的数量。
  • weights: 每个物品的重量数组。
  • values: 每个物品的价值数组。
  • m: 背包的最大容量。
    // 定义 dp 数组
    int[][] dp = new int[n + 1][m + 1];
  • 初始化一个二维数组 dpdp[i][j] 表示前 i 个物品放入容量为 j 的背包时的最大总价值。

主循环:遍历每个物品

    for (int i = 1; i <= n; i++) {
        int weight = weights[i - 1];
        int value = values[i - 1];
  • 使用 i 遍历每个物品。weights[i - 1]values[i - 1] 用来获取当前物品的重量和价值(由于数组索引从0开始,所以下标减1)。

内层循环:遍历每个容量

        for (int j = 0; j <= m; j++) {
  • 使用 j 遍历从 0m 的每个可能的背包容量。
            // 如果不选当前物品
            dp[i][j] = dp[i - 1][j];
  • 如果不选择当前物品 i,则最大价值就是前 i-1 个物品在容量 j 下的最大价值。
            // 如果选择当前物品
            if (j >= weight) {
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value);
            }
  • 如果选择当前物品并且当前容量 j 大于等于该物品的重量,程序会考虑两种选择:
    1. 不选择物品 i,保持 dp[i][j] 的值。
    2. 选择物品 i,则可以得到的价值为 dp[i - 1][j - weight] + value,这即为放弃当前物品 j - weight 的最大价值加上当前物品的价值。
  • 使用 Math.max 选择这两种方案中的最大值。

返回结果

    return dp[n][m];
}
  • 最终返回 dp[n][m],即所有 n 个物品放入容量为 m 的背包时的最大价值。

总结

这个算法的时间复杂度为 O(n * m),因为有两个嵌套的循环分别遍历物品数和背包容量。空间复杂度也是 O(n * m),用于存储 dp 数组。如果需要进一步优化空间,也可以使用一维数组,但这就需要额外的技巧以保持正确性。在实际使用中,这种动态规划方法非常有效,尤其是在物品数量与容量的范围较小时。

这段代码实现了著名的0-1背包问题的动态规划解法。下面详细解释背包问题的原理、代码的逻辑以及如何使用动态规划解决这个问题。

0-1背包问题简介

0-1背包问题是一个优化问题,给定一组物品,每个物品有一定的重量和价值,以及一个背包的最大承重,目标是选择物品,使得它们的总价值最大化,并且总重量不超过背包的承重限制。物品数量为有限,且每个物品只能选择一次。

代码逻辑

  1. 定义动态规划数组

    int[][] dp = new int[n + 1][m + 1];
    
    • dp[i][j] 表示前 i 个物品在承重为 j 的情况下能够达到的最大价值。
    • 数组的大小是 (n + 1) x (m + 1),其中 n 是物品数量,m 是背包的最大承重。+1 是为了考虑0个物品和0承重的情况。
  2. 遍历每个物品

    for (int i = 1; i <= n; i++) {
        int weight = weights[i - 1];
        int value = values[i - 1];
        ...
    }
    
    • 从 1 到 n 遍历每个物品。由于 dp 数组的维度是 n + 1,这里从 1 开始方便处理0个物品的情况。
  3. 遍历每个容量

    for (int j = 0; j <= m; j++) {
    
    • 对于每个可用的承重 j,首先假设不选择当前物品,即 dp[i][j] = dp[i - 1][j]
  4. 选择当前物品

    if (j >= weight) {
        dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - weight] + value);
    }
    
    • 如果当前物品可以加入背包(即当前承重 j 大于等于物品的重量),则计算选择该物品后的总价值:
      • dp[i - 1][j - weight] + value 表示选择该物品后,剩余承重 j - weight 中能获得的最大价值加上当前物品的价值。
    • 更新 dp[i][j] 为不选和选择当前物品的最大价值。
  5. 返回结果

    return dp[n][m];
    
    • 最终 dp[n][m] 代表使用前 n 个物品在承重最大为 m 的情况下能够获得的最大价值。

复杂度分析

  • 时间复杂度:O(n * m),其中 n 是物品的数量,m 是背包的最大承重。需要遍历所有物品和所有承重组合。
  • 空间复杂度:O(n * m),用于存储动态规划表。如果需要节省空间,可以考虑用滚动数组优化。

总结

通过动态规划的方法,我们能够有效地解决0-1背包问题。代码利用二维数组 dp 来保存各个状态的信息,从而确保在后续阶段能利用之前的结果,并最终给出最大的包裹价值。