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

79 阅读3分钟

问题描述

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

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


测试样例

样例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

问题分析

对于第i个物品来说,它都有选取和未选取两种状态。如果选取,则背包容量减少,如果未选,则背包容量不变。而它的子问题就是在剩余背包空间left下前i-1个物品能够得到的最大价值之和。

代码实现

很容易地想到用递归的方式处理这类问题,那么确认递归的条件与终点是很重要的。对于每一次调用函数,我们需要返回的值是它的两个子问题中(即选取该元素后的情况与未选取该元素后的情况)的最大值。这里递归的终点有两种情况,一种是此时已经没有物品可以选了,那么自然可以进行return,还有一种可能是剩余背包空间为负数,很容易想到此时是一个不合法的情况,应当返回一个较小的数,如负数标记它为不合法,这样在下面求max时自然而然地可以去掉这个不合法的情况。

写完递归的函数之后,就可以传入值了,我这里是从最后一个物品开始选取,当然从第一个开始选取也是可以的。

  • 时间复杂度O(n * m)
  • 空间复杂度O(n * m)

本题总结

背包问题是一个经典的组合优化问题,目标是选择一组物品放入背包,使得在不超过背包容量的情况下,背包中物品的总价值最大。

我们可以通过定义递归函数 f(i, left),表示在剩余容量 left下,从第 i 个物品到第 0 个物品的最大总价值。

与此同时,我们可以使用记忆化搜索,对于已经计算出的结果进行储存,减少不必要的二次计算,从而节约内存与时间。

个人思考

递归方法直观易懂,但容易导致重复计算。通过记忆化搜索,可以显著减少计算量。 动态规划的核心在于状态转移,通过递归函数 f(i, left) 的状态转移,可以逐步构建最优解。

改进方面

可以使用动态规划的思想,将递归函数翻译为数组从而实现进一步节约内存。需要注意的是在数组中代表的不是某个特定点的值,而是代表这个过程中的某个值。关于这一点我在后面会提及到。