如何降低类背包变体子集优化问题的解决方案时间复杂度?
背包问题变体优化求解:动态规划+剪枝方案
问题定义
- 给定由正整数元组
(a, b)组成的列表,需挑选部分元素生成新列表,在满足阈值条件的前提下最大化选中元素的a值总和。 - 阈值判断公式:
((sumOfAllB × (weight − sharedWeight) + sharedWeight) / k) ≥ threshold,其中sumOfAllB是选中元素的b值总和,k是选中元素的数量,weight和sharedWeight为给定常量整数。 - 当前暴力枚举的时间复杂度为
O(2ⁿ),需要更高效的实现方式。
示例数据
const exampleQueue = [ {a: 10, b: 4}, {a: 7, b: 10}, {a: 3, b: 6}, {a: 1, b: 4}, {a: 1, b: 1}, ] // 以下是几种选中状态(1表示选中,0表示未选中) const mappedToA = [10, 7, 3, 1, 1] // 不满足阈值 const firstAttempt = [10, 7, 3, 1, 0] const secondAttempt = [10, 7, 3, 0, 1] const thirdAttempt = [10, 7, 3, 0, 0] const fourthAttempt = [10, 7, 0, 1, 1] // ...
优化方案:动态规划+剪枝
第一步:转化条件,缩小搜索范围
先把阈值公式变形简化:
原公式整理后可得:sumB*(weight - sharedWeight) ≥ k*threshold - sharedWeight
记C = weight - sharedWeight,D = -sharedWeight,公式可简化为:C*sumB + D ≥ k*threshold(若C=0需单独处理)
接着确定可选的元素数量范围:
- 遍历
k从1到总元素数n,找出所有可能的k值:排除那些无论选哪k个元素都无法满足阈值的情况,缩小后续搜索的范围。 - 你提到的「计算需移除的最少元素数量」,本质是找到最大的有效
k值(即n - 最少移除数),从这个k开始向下遍历——因为选中元素越多,a总和可能越大,优先检查高k值的情况能更快找到最优解。
第二步:动态规划状态设计
定义DP[k][s] = 选中k个元素时,能达到的最大a总和,其中s是这些元素的b值总和。
- 状态转移:对每个元素
(a_i, b_i),从当前最大可能的k值倒序遍历到1,再遍历所有已记录的s值,更新DP[k][s + b_i] = max(DP[k][s + b_i], DP[k-1][s] + a_i)。 - 初始状态:
DP[0][0] = 0,其余状态设为不可达(比如用-∞标记)。
第三步:剪枝与结果筛选
- 同一
k值下,若两个状态的s(b总和)相同,直接丢弃a总和较小的那个——我们的目标是最大化a,小值状态没有保留意义。 - 遍历所有有效
k值,对每个k检查DP[k]中的所有s是否满足阈值条件,记录满足条件的最大a总和。
时间复杂度说明
假设总元素数为n,最大有效k值为max_k,所有元素b值总和为max_sumB,则时间复杂度为O(n*max_k*max_sumB),远优于暴力枚举的O(2ⁿ),在n≤50的场景下完全可行。
补充提示
如果weight - sharedWeight为负数,阈值公式的不等号方向会反转,需要调整判断逻辑;若元素b值较大,可考虑对b值离散化,或用滚动数组优化DP的空间占用。
内容的提问来源于stack exchange,提问作者MManke
相关产品推荐
相关产品推荐

