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

108 阅读4分钟

问题描述

一个旅行者外出旅行时需要将 n 件物品装入背包,背包的总容量为 m。每个物品都有一个重量和一个价值。你需要根据这些物品的重量和价值,决定如何选择物品放入背包,使得在不超过总容量的情况下,背包中物品的总价值最大。

给定两个整数数组 weights 和 values,其中 weights[i] 表示第 i 个物品的重量,values[i] 表示第 i 个物品的价值。你需要输出在满足背包总容量为 m 的情况下,背包中物品的最大总价值。

数据结构选择

  • 动态规划数组:我们选择一个一维数组 dp,其中 dp[i] 表示在背包容量为 i 时的最大价值。

算法步骤

  1. 初始化

    • 创建一个长度为 m+1 的数组 dp,所有元素初始化为 0。这个数组用于存储在不同背包容量下的最大价值。
  2. 状态转移

    • 外层循环:遍历每个物品。对于每个物品 i,我们需要考虑是否将其放入背包。

    • 内层循环:从背包容量 m 递减到当前物品的重量 weights[i]。这样做是为了确保在更新 dp[j] 时,dp[j - weights[i]] 还没有被当前物品更新过,从而避免重复计算。

    • 状态转移方程:对于每个容量 j,更新 dp[j] 为 max(dp[j], dp[j - weights[i]] + values[i])。这个方程的意思是:

      • dp[j]:不选择当前物品,保持当前容量 j 的最大价值。
      • dp[j - weights[i]] + values[i]:选择当前物品,容量减少 weights[i],价值增加 values[i]
  3. 返回结果

    • 最终返回 dp[m],即在背包容量为 m 时的最大价值。

复杂度分析

  • 时间复杂度O(n * m),其中 n 是物品数量,m 是背包容量。我们需要遍历每个物品,并对每个物品遍历从 m 到 weights[i] 的所有容量。
  • 空间复杂度O(m),我们只使用了一个长度为 m+1 的数组 dp

测试样例

样例1:

输入:n = 3 ,weights = [2, 1, 3] ,values = [4, 2, 3] ,m = 3
输出:6

样例2:

输入:n = 4 ,weights = [1, 2, 3, 2] ,values = [10, 20, 30, 40] ,m = 5
输出:70

样例3:

输入:n = 2 ,weights = [1, 4] ,values = [5, 10] ,m = 4
输出:10

详细步骤示例

以样例1为例:

  • 输入n = 3 ,weights = [2, 1, 3] ,values = [4, 2, 3] ,m = 3
  • 初始化dp = [0, 0, 0, 0]

第一轮(物品0)

  • weights[0] = 2values[0] = 4

  • 更新 dp[2] 到 dp[3]

    • dp[2] = max(dp[2], dp[0] + 4) = max(0, 4) = 4
    • dp[3] = max(dp[3], dp[1] + 4) = max(0, 4) = 4
  • 更新后:dp = [0, 0, 4, 4]

第二轮(物品1)

  • weights[1] = 1values[1] = 2

  • 更新 dp[1] 到 dp[3]

    • dp[1] = max(dp[1], dp[0] + 2) = max(0, 2) = 2
    • dp[2] = max(dp[2], dp[1] + 2) = max(4, 4) = 4
    • dp[3] = max(dp[3], dp[2] + 2) = max(4, 6) = 6
  • 更新后:dp = [0, 2, 4, 6]

第三轮(物品2)

  • weights[2] = 3values[2] = 3

  • 更新 dp[3]

    • dp[3] = max(dp[3], dp[0] + 3) = max(6, 3) = 6
  • 更新后:dp = [0, 2, 4, 6]

最终结果:dp[3] = 6

以下为golang版本代码,暂未评测(还没上线)

package main

import "fmt"

func solution(n int, weights []int, values []int, m int) int {
    // 01背包原题
    // dp[i]表示在容量为i时的最大价值
    dp := make([]int, m+1)
    for i := 0; i < n; i++ {
       for j := m; j >= weights[i]; j-- {
          dp[j] = max(dp[j], dp[j-weights[i]]+values[i])
       }
    }
    return dp[m]
}

func main() {
    fmt.Println(solution(3, []int{2, 1, 3}, []int{4, 2, 3}, 3) == 6)
    fmt.Println(solution(4, []int{1, 2, 3, 2}, []int{10, 20, 30, 40}, 5) == 70)
    fmt.Println(solution(2, []int{1, 4}, []int{5, 10}, 4) == 10)
}

以下为python版本代码,由marscode转化而来,已通过评测

def solution(n, weights, values, m):
    # dp[i]表示在容量为i时的最大价值
    dp = [0] * (m + 1)
    for i in range(n):
        for j in range(m, weights[i] - 1, -1):
            dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
    return dp[m]

# 测试样例
print(solution(3, [2, 1, 3], [4, 2, 3], 3) == 6)
print(solution(4, [1, 2, 3, 2], [10, 20, 30, 40], 5) == 70)
print(solution(2, [1, 4], [5, 10], 4) == 10)

总结

这是一道典型的01背包问题,甚至没有一点点变形,就是原题。可以看看代码随想录里的背包讲解,看完就可以了。