问题描述
一个旅行者外出旅行时需要将 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) 的状态转移,可以逐步构建最优解。
改进方面
可以使用动态规划的思想,将递归函数翻译为数组从而实现进一步节约内存。需要注意的是在数组中代表的不是某个特定点的值,而是代表这个过程中的某个值。关于这一点我在后面会提及到。