因为单调队列按索引递增、按有效值(如dp[j]−k×j)单调递减维护候选,淘汰值更小且位置更靠前的冗余状态,确保队首恒为当前窗口最优决策;入队前须从队尾弹出所有不优于当前j的有效值,出队则先检查队首索引是否超出左边界再计算dp[i]。

为什么单调队列能砍掉冗余状态?
因为很多 dp[j] 在后续转移中永远用不上——它们值更小、位置更靠前,既不如新来的候选者大,又比新来的老,自然该淘汰。单调队列维护的是「按索引递增、按 dp[j] - k * j(或类似偏移形式)递减」的候选集合,保证队首永远是当前窗口内最优的 j。
怎么写入队逻辑才不漏不冗?
入队前必须从队尾开始弹出所有「不优于当前 j」的旧状态。常见错误是只比较 dp[j],但实际要比较的是转移表达式里的有效值,比如 dp[j] - j(当转移形如 dp[i] = min(dp[j] + i - j) 时)。否则会把一个 dp[j] 小但 dp[j] - j 大的合法候选误删。
- 先算出当前待入队的“权重值”,例如
val = dp[j] - k * j - while 队非空且队尾权重 ≤
val:pop_back() - push_back(j)
滑窗边界和出队时机怎么卡准?
出队不是看 i - j > L 就直接 pop_front(),而是每次在计算 dp[i] 前,先检查队首索引是否已超出左边界(比如 q.front() ),再弹出。漏这一步会导致用到越界的 <code>j;多弹或早弹则可能提前丢掉还在窗口内的有效状态。
典型场景:dp[i] = min{dp[j] + cost(i,j)} for j ∈ [i-L, i-1],此时左边界是 i - L,判断条件必须是 q.front() ,不是 <code> 或其他变体。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
不同转移形式下权重表达式怎么推?
权重不是固定的,它取决于 cost(i,j) 能否拆成只含 i 和只含 j 的两部分。例如:
-
dp[i] = min(dp[j] + i + j)→ 权重为dp[j] + j,队列单调递减 -
dp[i] = min(dp[j] + (i-j)^2)→ 无法线性拆分,单调队列失效,得换斜率优化或李超树 -
dp[i] = min(dp[j]) + a[i](j ∈ [i-L, i-1])→ 权重就是dp[j],最简情形
一旦发现 cost(i,j) 含 i*j 或 abs(i-j) 等耦合项,基本可以确定不能直接上单调队列——这时候回头检查是不是误用了模型,比硬调队列逻辑更省时间。

















