资讯动态

动态规划多重背包问题的单调队列优化详解

发布时间:2026/10/27 22:49:29 来源:尧图企业网站定制
1. 多重背包问题与单调队列优化概述多重背包问题是动态规划中的经典问题它介于01背包和完全背包之间。具体描述为给定n种物品和一个容量为V的背包第i种物品最多有s_i件可用每件体积是w_i价值是v_i。我们需要选择装入背包的物品使得总体积不超过背包容量且总价值最大。传统解法使用三重循环dp [0] * (V 1) for i in range(n): # 遍历物品 for j in range(V, w[i] - 1, -1): # 遍历背包容量 for k in range(1, min(s[i], j // w[i]) 1): # 遍历物品数量 dp[j] max(dp[j], dp[j - k*w[i]] k*v[i])这种方法时间复杂度为O(nVS)当数据量较大时效率很低。2. 单调队列优化原理详解2.1 状态转移方程的重新表述我们发现对于每个物品idp[j]只与dp[j - k*w[i]]有关其中k∈[1, s_i]。可以将状态按w_i的余数分类对于余数相同的j它们之间可以形成独立的转移链。设j m*w_i r其中r j % w_i则状态转移方程可以改写为dp[m*w_i r] max{dp[k*w_i r] (m-k)*v_i} 其中k∈[m-s_i, m]2.2 单调队列的引入对于每个余数r我们维护一个单调队列队列中存储的是可能成为最优解的候选状态。具体来说队列中的元素k应该满足k在窗口[m-s_i, m]内dp[kw_i r] - kv_i尽可能大队列维护两个性质单调性dp[kw_i r] - kv_i递减时效性k在有效窗口内2.3 具体实现步骤按余数r分类处理对于每个r初始化单调队列遍历m从0到(V-r)/w_i维护队列的单调性和时效性计算当前最优值3. 优化后的算法实现3.1 Python实现代码def multiple_knapsack(n, V, w, v, s): dp [0] * (V 1) for i in range(n): for r in range(w[i]): q [] for m in range((V - r) // w[i] 1): current m * w[i] r # 维护队列时效性 while q and m - q[0] s[i]: q.pop(0) # 计算候选值 candidate dp[current] - m * v[i] # 维护队列单调性 while q and dp[q[-1]*w[i]r] - q[-1]*v[i] candidate: q.pop() q.append(m) # 更新dp值 if q: dp[current] max(dp[current], dp[q[0]*w[i]r] (m - q[0]) * v[i]) return dp[V]3.2 复杂度分析优化后算法的时间复杂度降为O(n*V)空间复杂度为O(V)。相比原始的三重循环效率有了显著提升。4. 实际应用中的注意事项4.1 边界条件处理当w[i]0时需特殊处理确保队列不为空时再访问队首元素注意物品数量s[i]可能为0的情况4.2 性能优化技巧预处理物品合并相同w和v的物品二进制优化将多重背包转化为01背包滚动数组减少空间使用4.3 常见错误排查队列维护顺序错误应先处理时效性再处理单调性余数循环范围错误应为range(w[i])而非range(min(w[i], V))物品数量限制未正确应用确保k在[m-s_i, m]范围内5. 与其他优化方法的对比5.1 与二进制优化的比较二进制优化将多重背包转化为O(logS)个01背包问题时间复杂度为O(nVlogS)。当S较大时单调队列优化更优。5.2 与滑动窗口优化的关系单调队列优化本质上是滑动窗口优化在背包问题中的应用但需要结合背包问题的特殊性质进行调整。5.3 适用场景分析单调队列优化适合单个物品数量较大的情况二进制优化适合物品数量差异大的情况当V较小时原始方法可能更简单直接6. 扩展应用与变种问题6.1 多维背包问题对于多维背包如同时限制体积和重量单调队列优化可以沿一个维度进行另一个维度仍使用传统方法。6.2 分组背包问题当物品分为若干组每组只能选一个物品时可以在组内使用单调队列优化。6.3 带依赖的背包问题对于树形依赖的背包问题可以在DFS过程中对每个节点应用单调队列优化。在实际编码比赛中我通常会先写出朴素解法确保正确性然后再考虑是否适用单调队列优化。特别是在物品数量限制较大如s_i≥100时这种优化能带来显著的性能提升。一个常见的调试技巧是打印出单调队列在每次迭代时的状态这能帮助快速定位问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑