Complexity budget
time=(#states)×(transition cost).
| Total | Feasible n |
|---|
| O(n) | 107 |
| O(nlogn) | 106 |
| O(nn) | 105 |
| O(n2) | 5000 |
| O(n2logn) | 2000 |
| O(n3) | 400 |
| O(2nn) | 22 |
| O(2nn2) | 18 |
| O(3n) | 16 |
| O(n!) | 11 |
State shape by problem type
| Problem smells like | State |
|---|
| a prefix decision | dp[i] |
| two sequences | dp[i][j] |
| a contiguous range | dp[l][r] |
| items with a budget | dp[i][w] |
| a subtree | dp[v], often dp[v][0/1] |
| a subset (n≤22) | dp[mask] |
| digits of a number | dp[pos][tight][started][...] |
| a grid filled cell by cell | dp[row][profile] |
| a position with a last choice | dp[i][last] |
| an amount of a resource used | dp[i][used] |
Optimisation lookup
| Transition | Requirement | Technique | Result |
|---|
| minjdp[j] over a fixed window | none | monotone queue | O(n) |
| minjdp[j] over an arbitrary range | none | segment tree | O(nlogn) |
| minj(dp[j]+mjxi) | linear in x | CHT / Li Chao | O(n) / O(nlogn) |
| mink(dp[i−1][k]+C(k,j)) | monotone argmin | D&C DP | O(knlogn) |
| mink(dp[l][k]+dp[k][r]+C) | quadrangle inequality | Knuth | O(n2) |
| ”exactly k groups”, k huge | convex in k | Aliens trick | O(nlogC) |
| value is a convex function | convexity | slope trick | O(nlogn) |
| linear, constant transition, huge n | linearity | matrix power | O(k3logn) |
| boolean feasibility | — | bitset | /64 |
| sum over all submasks | — | SOS DP | O(2nn) |
| row minima, totally monotone | Monge | SMAWK | O(n) |
Loop-direction rules
| DP | Direction |
|---|
| 0/1 knapsack, 1D array | weight decreasing |
| Unbounded knapsack, 1D array | weight increasing |
| Interval DP | by increasing length |
dp[l][r] from dp[l+1][r] | l decreasing |
| Tree DP | post-order (or reverse BFS order) |
| Bitmask DP | mask increasing |
| Coin change combinations | coin loop outside |
| Coin change permutations | amount loop outside |
Debugging checklist
- Is the state complete? Does anything outside the state affect the future?
- Is the state minimal? Is any dimension never actually used?
- Base cases: are unreachable states initialised to ±∞ rather than 0?
- Loop order: does every dependency precede its use?
- Overflow: is
long long needed? Are you taking % MOD after every addition and multiplication?
- Off by one: is
dp[i] “first i elements” or “up to index i”? Pick one and stay consistent.
- Multiple test cases: is everything reset?
- Recursion depth: could the memoised version overflow the stack?
- Answer extraction: is it
dp[n], or the max over the last row?
Sanity checks before submitting
- Run the DP against a brute force on random small inputs. This catches almost everything.
- Test n=1, n=0, all-equal values, and the maximum constraint.
- If the answer should be non-negative and your −∞ sentinel is −1018, check that adding to it never underflows.
See also: Dynamic Programming · Designing States · Complexity Cheatsheet