题目描述
这道题是典型的0-1 背包问题,目标是在不超过背包容量的情况下,选择物品使得总价值最大化。下面是解决这个问题的思路:
解决思路
-
问题建模
- 给定
n个物品,每个物品有重量weights[i]和价值values[i]。 - 背包容量为
m,要求在不超过容量的情况下选择物品,使得总价值最大。
- 给定
-
动态规划方法
- 使用一个一维动态规划数组
f,其中f[j]表示容量不超过j时可以获得的最大价值。 - 初始化
f数组的长度为m+1,每个位置初始值为 0,表示在背包没有物品时的初始价值。
- 使用一个一维动态规划数组
-
状态转移
- 遍历每个物品
i,对于每个物品的重量weights[i],在容量j从m到weights[i]之间逆序更新f数组。 - 使用逆序遍历是为了保证每个物品只被选择一次,即避免重复使用物品。
- 对于每个容量
j,更新公式为: f[j]=max(f[j],f[j−weights[i]]+values[i])f[j] = \max(f[j], f[j - weights[i]] + values[i])f[j]=max(f[j],f[j−weights[i]]+values[i]) 这个公式表示,如果选择当前物品i,那么在原有的最大价值f[j - weights[i]]的基础上加上当前物品的价值values[i],从而更新最大价值f[j]。
- 遍历每个物品
-
最终结果
- 当所有物品遍历完后,
f[m]就表示在背包容量不超过m的情况下可以获得的最大价值。
- 当所有物品遍历完后,
def solution(n: int, weights: list, values: list, m: int) -> int:
# 初始化动态规划数组,长度为 m+1,所有元素都设置为 0
f = [0] * (m + 1)
# 遍历每个物品
for i in range(n):
# 从 m 到 weights[i] 的逆序遍历,确保每个物品只被计算一次
for j in range(m, weights[i] - 1, -1):
# 更新 f 数组,存储在不超过重量 j 的情况下可以获得的最大价值
f[j] = max(f[j], f[j - weights[i]] + values[i])
# 返回在不超过重量 m 的情况下可以获得的最大价值
return f[m]
if __name__ == '__main__':
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)
时间复杂度分析
-
初始化数组:
f = [0] * (m + 1)初始化一个大小为
m+1的数组,时间复杂度是O(m)。 -
遍历每个物品:
for i in range(n): for j in range(m, weights[i] - 1, -1):对于每个物品
i(共有n个物品),我们需要遍历从m到weights[i]的容量值,即逆序遍历。最坏情况下,内层循环的遍历次数为m次,因此每个物品的时间复杂度为O(m)。 -
状态转移公式:
f[j] = max(f[j], f[j - weights[i]] + values[i])该操作的时间复杂度为
O(1),每次更新f[j]数组的元素。
总时间复杂度:
- 外层循环:
O(n) - 内层循环:
O(m)
因此,总的时间复杂度为 O(n * m)。
空间复杂度分析
- 我们只使用了一个大小为
m+1的数组f,来记录不同容量下的最大价值,因此空间复杂度是O(m)。
样例分析
样例 1
输入:
solution(3, [2, 1, 3], [4, 2, 3], 3)
- 物品数:3
- 重量:[2, 1, 3]
- 价值:[4, 2, 3]
- 背包容量:3
动态规划过程:
-
初始化
f = [0, 0, 0, 0],表示背包容量从 0 到 3 时的最大价值。 -
遍历第一个物品(重量 2,价值 4):
f[3] = max(f[3], f[3-2] + 4) = max(0, 0 + 4) = 4,更新f = [0, 0, 0, 4]。f[2] = max(f[2], f[2-2] + 4) = max(0, 0 + 4) = 4,更新f = [0, 0, 4, 4]。
-
遍历第二个物品(重量 1,价值 2):
f[3] = max(f[3], f[3-1] + 2) = max(4, 0 + 2) = 4,f[3]不变。f[2] = max(f[2], f[2-1] + 2) = max(4, 0 + 2) = 4,f[2]不变。f[1] = max(f[1], f[1-1] + 2) = max(0, 0 + 2) = 2,更新f = [0, 2, 4, 4]。
-
遍历第三个物品(重量 3,价值 3):
f[3] = max(f[3], f[3-3] + 3) = max(4, 0 + 3) = 4,f[3]不变。
最终结果为 f[3] = 6,表示在背包容量为 3 的情况下,能够获得的最大价值为 6。
输出:
True # 表示计算正确
样例 2
输入:
solution(4, [1, 2, 3, 2], [10, 20, 30, 40], 5)
- 物品数:4
- 重量:[1, 2, 3, 2]
- 价值:[10, 20, 30, 40]
- 背包容量:5
动态规划计算过程:
- 通过类似的过程,最终
f[5] = 70,表示在背包容量为 5 的情况下,能够获得的最大价值为 70。
输出:
True # 表示计算正确
样例 3
输入:
solution(2, [1, 4], [5, 10], 4)
- 物品数:2
- 重量:[1, 4]
- 价值:[5, 10]
- 背包容量:4
最终结果为 f[4] = 10,表示在背包容量为 4 的情况下,能够获得的最大价值为 10。
输出:
True # 表示计算正确
总结
- 时间复杂度为
O(n * m),其中n为物品数量,m为背包容量。 - 空间复杂度为
O(m),由于使用了一个大小为m+1的数组。 - 通过动态规划解决了 0-1 背包问题,保证了每个物品只被计算一次,最终返回在给定背包容量下能够获取的最大价值。
此问题常见于背包类问题的变种,适合用动态规划方法来解决,通过优化每个状态的计算,能够在合适的时间内获得最优解。