问题描述
一个旅行者外出旅行时需要将 n 件物品装入背包,背包的总容量为 m。每个物品都有一个重量和一个价值。你需要根据这些物品的重量和价值,决定如何选择物品放入背包,使得在不超过总容量的情况下,背包中物品的总价值最大。
给定两个整数数组 weights 和 values,其中 weights[i] 表示第 i 个物品的重量,values[i] 表示第 i 个物品的价值。你需要输出在满足背包总容量为 m 的情况下,背包中物品的最大总价值。
数据结构选择
- 动态规划数组:我们选择一个一维数组
dp,其中dp[i]表示在背包容量为i时的最大价值。
算法步骤
-
初始化:
- 创建一个长度为
m+1的数组dp,所有元素初始化为0。这个数组用于存储在不同背包容量下的最大价值。
- 创建一个长度为
-
状态转移:
-
外层循环:遍历每个物品。对于每个物品
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]。
-
-
返回结果:
- 最终返回
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] = 2,values[0] = 4 -
更新
dp[2]到dp[3]:dp[2] = max(dp[2], dp[0] + 4) = max(0, 4) = 4dp[3] = max(dp[3], dp[1] + 4) = max(0, 4) = 4
-
更新后:
dp = [0, 0, 4, 4]
第二轮(物品1) :
-
weights[1] = 1,values[1] = 2 -
更新
dp[1]到dp[3]:dp[1] = max(dp[1], dp[0] + 2) = max(0, 2) = 2dp[2] = max(dp[2], dp[1] + 2) = max(4, 4) = 4dp[3] = max(dp[3], dp[2] + 2) = max(4, 6) = 6
-
更新后:
dp = [0, 2, 4, 6]
第三轮(物品2) :
-
weights[2] = 3,values[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背包问题,甚至没有一点点变形,就是原题。可以看看代码随想录里的背包讲解,看完就可以了。