Optimise a DP whose transition is a minimum or maximum over a sliding window:
from to , using a monotonic deque.
The sliding window minimum
Keep a deque of indices whose dp values are increasing. Before pushing , pop everything at the back with a value — those can never be the minimum again, because is both newer and better. Before querying, pop the front while it has fallen out of the window.
deque<int> dq;
for (int i = 0; i <= n; i++) {
while (!dq.empty() && dq.front() < i - k) dq.pop_front(); // out of window
dp[i] = (i == 0) ? 0 : dp[dq.front()] + cost(i);
while (!dq.empty() && dp[dq.back()] >= dp[i]) dq.pop_back(); // dominated
dq.push_back(i);
}Each index enters and leaves the deque once, so the total work is .
When the window is not a fixed width
The technique needs only that the valid range of is a contiguous interval that moves monotonically with . Common cases:
| Condition on | Window |
|---|---|
| fixed width | |
| two pointers determine the left end | |
| (sorted ) | two pointers |
where lastBad is non-decreasing | two pointers |
If the left end can move backwards, the deque is invalid — use a segment tree () or a different optimisation.
Adding a term that depends on
If the transition is , store in the deque instead of . The deque only needs the quantity being minimised to be computable at push time.
If instead the term couples and multiplicatively, the deque fails and you want CHT:
| Transition shape | Technique |
|---|---|
| over a window | monotone queue, |
| over a window | monotone queue, |
| CHT | |
| , Monge | D&C DP |
| over an arbitrary range | segment tree, |
Classic uses
Jump game with a bounded jump. dp[i] = min(dp[j]) + cost(i) for — the textbook case.
Bounded knapsack in . Group weights by residue class mod ; within each class the transition is a sliding-window maximum of over . This is how bounded knapsack achieves instead of — see Knapsack.
Maximum subarray with a length limit. max(pref[i] - min(pref[j])) over a window.
Constrained partitioning. “Split the array into pieces of length between and , minimising the total cost” — the window is .
The standalone structure
Sliding window minimum is useful outside DP too — see Monotonic Queue. It is also the -amortized way to implement a minimum queue, which is what makes Mo’s algorithm variants and certain graph algorithms fast.
Debugging checklist
- Is the window’s left end monotone non-decreasing? If not, the deque is wrong.
- Are you popping the front before querying and the back before pushing?
- Is the comparison
>=(keeping the newest among equals) rather than>? Either is correct for the value, but>=keeps the deque shorter. - Is
dp[i]pushed after it is computed? Pushing before creates a self-dependency.
See also: Monotonic Queue · Convex Hull Trick · Knapsack