背包问题动态规划:两段相似实现代码为何输出不同结果?
0-1背包问题递归解法错误原因分析
问题背景
我需要解决0-1背包问题:在不超过背包容量W的前提下最大化物品总价值。参数定义:
- n:物品数量
- W:背包重量容量
- wt:n个物品的重量数组
- val:n个物品的价值数组
我实现了两段递归+记忆化的代码,第一段返回正确结果216,但第二段返回错误结果205(小测试用例下两段代码均正常运行)。两段代码思路一致,求解释为何第二段在以下测试用例中输出错误?
测试用例
n = 29 W = 41 val = [57, 95, 13, 29, 1, 99, 34, 77, 61, 23, 24, 70, 73, 88, 33, 61, 43, 5, 41, 63, 8, 67, 20, 72, 98, 59, 46, 58, 64] wt = [83, 84, 85, 76, 13, 87, 2, 23, 33, 82, 79, 100, 88, 85, 91, 78, 83, 44, 4, 50, 11, 68, 90, 88, 73, 83, 46, 16, 7]
代码1(正确)
#Function to return max value that can be put in knapsack of capacity W. def knapSack(W, wt, val, n): # code here def hlpr(W, wt, val, n, d): if (n == 0): return 0 if ((W, n) in d.keys()): return(d[(W, n)]) if (W < wt[n-1]): d[(W, n)] = hlpr(W, wt, val, n-1, d) else: d[(W, n)] = max(val[n-1] + hlpr(W-wt[n-1], wt, val, n - 1, d), hlpr(W, wt, val, n-1, d)) return(d[(W, n)]) d = {} return(hlpr(W, wt, val, n, d))
代码2(错误)
def knapSack(W, wt, val, n): # code here def helper(W, wt, val, n, d, op): if(n == 0): return op if((W, n) in d): return d[(W, n)] if(W - wt[n-1] < 0): d[(W, n)] = helper(W, wt, val, n-1,d, op) return d[(W, n)] d[(W, n)] = max(helper(W-wt[n-1], wt, val, n-1, d, op+val[n-1]), helper(W, wt, val, n-1, d, op)) return d[(W, n)] d = {} return helper(W, wt, val, n, d, 0)
错误原因分析
两段代码的核心差异在于记忆化存储的逻辑和递归返回值的含义:
代码1的逻辑:
- 递归函数
hlpr(W, n)返回的是「剩余容量为W、考虑前n个物品时,能获得的最大价值」,这个值是独立于当前递归路径的固定值。 - 记忆化字典
d存储的是(W, n)对应的最大价值,无论从哪个路径到达这个状态,结果都是一致的,所以缓存有效。
- 递归函数
代码2的逻辑:
- 递归函数
helper(W, n, op)把当前累计价值op作为参数传递,试图通过op累加来计算总价值。但这里犯了致命错误:记忆化存储的d[(W, n)]是某个特定op下的结果,而不是该状态本身的最大价值。 - 举个例子:当不同递归路径到达同一个
(W, n)状态时,携带的op值可能不同。比如第一次到达(W, n)时op=100,计算出结果后存入d;但后续另一个路径到达(W, n)时op=80,此时直接返回缓存的d[(W, n)](基于op=100的结果),这就导致计算错误——因为这个状态的最大价值应该是基于当前op加上后续的最大收益,而不是复用之前路径的累计结果。 - 简单说,代码2的记忆化缓存了「特定累计价值下的最终结果」,而不是「当前状态本身的最大收益」,这违背了记忆化搜索的核心:同一个状态的结果是固定的,与到达路径无关。
- 递归函数
验证说明
小测试用例下代码2看似正常,是因为小场景中不同路径到达同一(W, n)状态时,op值可能恰好相同,或者缓存的错误结果没有影响最终最大值。但在复杂测试用例中,不同路径的op差异会导致缓存的结果失效,最终输出错误的总价值。
内容的提问来源于stack exchange,提问作者Malyada N
相关产品推荐
相关产品推荐

