背包最大价值 | 豆包MarsCode AI刷题

61 阅读4分钟

1. 问题背景与建模

旅行背包问题是经典的动态规划问题之一,被称为“01背包问题”。其核心在于如何在有限容量的背包中装入若干物品,使得总价值最大化。该问题不仅广泛应用于学术研究中,还具有许多现实意义,例如行李打包、资源分配、预算管理等。

问题定义

  • 给定 n 件物品,每件物品有一个 重量价值
  • 背包容量为 m,意味着背包最多能承载重量为 m 的物品。
  • 每件物品只能被选择一次(01背包特点)。
  • 我们需要决定哪些物品放入背包,使得 总重量不超过 m 的情况下,总价值最大化。

输入输出

  • 输入:两个数组 weights(物品重量)和 values(物品价值),以及背包容量 m
  • 输出:最大总价值。

2. 分析与思考

解决该问题需要综合考虑以下两个限制因素:

  1. 容量限制:装入背包的物品总重量不能超过容量 m
  2. 价值最大化:装入的物品应尽可能提升总价值。

由于无法直接穷举所有可能的组合(组合数为 (2^n),当 (n) 较大时不可行),我们需要更高效的算法。动态规划是解决此类问题的理想工具。

3. 动态规划思路

3.1 状态定义

dp[j] 表示在背包容量为 j 时,能够获得的最大总价值。这里,j 的范围是从 0m

  • dp[0] 表示背包容量为 0 时的最大价值(始终为 0)。
  • dp[m] 表示背包容量为 m 时的最大价值,即我们要求解的目标。

3.2 转移方程

对于每件物品,我们有两个选择:

  1. 不选该物品:即不将当前物品装入背包,状态转移为 dp[j]
  2. 选择该物品:即将当前物品装入背包,状态转移为 dp[j - weights[i]] + values[i],其中 j - weights[i] 是剩余容量,values[i] 是当前物品的价值。

因此,状态转移方程为: [ dp[j] = \max(dp[j], dp[j - weights[i]] + values[i]) ]

3.3 初始化与边界条件

  • 如果背包容量为 0(即 j=0),则无法装入任何物品,最大价值为 0,即 dp[0] = 0
  • 初始 dp 数组的所有元素应为 0。

3.4 遍历顺序

  • 物品遍历:逐一考虑每件物品。
  • 容量遍历:为防止重复计算,容量从大到小遍历(保证当前物品只被选择一次)。

4. 实现与分析

4.1 实现代码

以下是基于动态规划的代码实现:

python
 代码解读
复制代码
def knapsack(weights, values, m):
    # 初始化 dp 数组
    dp = [0] * (m + 1)
    
    # 遍历每个物品
    for i in range(len(weights)):
        # 从背包容量 m 递减到物品重量
        for j in range(m, weights[i] - 1, -1):
            # 状态转移
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
    
    return dp[m]  # 返回背包容量为 m 时的最大价值

4.2 复杂度分析

  • 时间复杂度:动态规划的核心是双重循环。外层遍历物品数量 n,内层遍历容量 m,因此时间复杂度为 (O(n \cdot m))。
  • 空间复杂度:我们只使用一个一维数组 dp,因此空间复杂度为 (O(m))。

5. 举例说明

示例输入:

plaintext
 代码解读
复制代码
weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
m = 5

解题步骤:

  1. 初始化 dp 数组为 [0, 0, 0, 0, 0, 0](长度为 m+1)。

  2. 考虑第 1 件物品(重量 2,价值 3):

    • 更新 dp[5]dp[2]

      plaintext
       代码解读
      复制代码
      dp = [0, 0, 3, 3, 3, 3]
      
  3. 考虑第 2 件物品(重量 3,价值 4):

    • 更新 dp[5]dp[3]

      plaintext
       代码解读
      复制代码
      dp = [0, 0, 3, 4, 4, 7]
      
  4. 考虑第 3 件物品(重量 4,价值 5):

    • 更新 dp[5]dp[4]

      plaintext
       代码解读
      复制代码
      dp = [0, 0, 3, 4, 5, 7]
      
  5. 考虑第 4 件物品(重量 5,价值 6):

    • 更新 dp[5]

      plaintext
       代码解读
      复制代码
      dp = [0, 0, 3, 4, 5, 7]
      
  6. 最终结果为 dp[5] = 7

6. 总结与启发

01背包问题虽然是一道经典的算法题,但它的解决思路远不局限于“写出正确代码”。在解决问题的过程中,我们需要:

  1. 从问题建模开始,明确输入、输出和约束条件。
  2. 分析解法的核心思路,找出限制因素与优化目标。
  3. 权衡解法优劣,结合实际需求选择合适的算法。

更重要的是,背包问题的思想广泛应用于现实场景,例如资源分配、预算优化等。在这些应用中,动态规划的思维和解决方法,能够帮助我们找到更高效、更科学的解决方案。