预算支出最大化动态规划实现非最优问题排查
问题定位
你推导的递推方程完全正确,这个问题是经典的0-1背包问题——你没快速匹配到原型,是因为这个场景下每个项目的“价值”等于自身成本,目标是总价值(总支出)尽可能接近背包容量(总预算),和教材里“重量、价值分离”的常规背包描述有细微差异。
你的实现错误和递推逻辑无关,核心是一维滚动数组的遍历逻辑写错了。
错误原因分析
你设计的一维记忆数组C、从右向左填充的思路本身是0-1背包的标准空间优化方案,但你实现时犯了关键错误:
- 错误维护了一个全局的剩余预算变量,处理项目时只跟踪“选当前项目”这单一路径的预算变化,完全丢弃了“不选当前项目”的并行状态,本质退化成了“遇到能装就装”的贪心算法,自然无法得到全局最优。
- 额外的细节错误:你提到数组
C大小等于B,对应预算0到B的话数组长度需要是B+1,长度为B时最大索引是B-1,本身就无法存储预算满额B的结果。
正确实现步骤
严格按照递推关系落地一维DP的逻辑即可,核心是两层循环的嵌套顺序和遍历方向:
- 初始化长度为
B+1的数组C,所有元素初始值为0,C[b]表示预算为b时能实现的最大支出 - 外层循环:逐个遍历所有项目
p_i,遍历顺序不影响最终结果 - 内层循环:从最大预算B开始倒序遍历,直到预算值等于当前项目成本p_i为止,对每个预算档位
b执行更新:C[b] = max(C[b], C[b - p_i] + p_i)- 倒序遍历的作用是保证更新
C[b]时,用到的C[b-p_i]是处理上一个项目时的留存状态,不会出现同一个项目被重复选中的问题
- 倒序遍历的作用是保证更新
- 所有项目遍历完成后,
C[B]就是所求的最大支出值
测试用例推演
用你给出的B=50、P=[10,20,40]用例走一遍流程就能验证正确性:
初始状态:
C是长度51的全0数组,索引0~50对应0到50的预算档位
- 处理第一个项目p=10:
从b=50倒序到10更新,所有b>=10的位置C[b] = max(0, C[b-10]+10) = 10- 处理第二个项目p=20:
从b=50倒序到20更新,例如b=50时取max(10, C[30]+20=30)得30;b=30时取max(10, C[10]+20=30)得30。处理完成后,b>=30档位值为30,20<=b<30档位值为20,10<=b<20档位值为10- 处理第三个项目p=40:
从b=50倒序到40更新:b=50时取max(30, C[10]+40=50)得50;b=40时取max(30, C[0]+40=40)得40
最终C[50] = 50,和最优解一致
实现注意事项
- 绝对不要用全局变量跟踪单条路径的剩余预算:一维DP数组的核心是同时保存所有可能预算档位下的最优值,覆盖“选/不选当前项目”的所有分支状态
- 内层循环必须倒序:如果改成正序遍历,会变成允许同一个项目重复选取的完全背包逻辑,不符合项目只能选一次的要求
- 内层循环不需要遍历到0:当预算小于当前项目成本时,项目无法被选中,对应位置的值不需要更新,遍历到p_i即可减少无效计算
内容的提问来源于stack exchange,提问作者Lightsong
相关产品推荐
相关产品推荐

