Learning/Dp 1d

1-D Dynamic Programming

12 questions. Every one is the same pipeline; what varies is the recurrence, the combiner, and whether the state can be compressed.

The two properties that license DP

The same subproblem, computed again and again
The same subproblem, computed again and again

  1. Optimal substructure — the answer is built from answers to smaller inputs
  2. Overlapping subproblems — the same smaller input recurs

Without (2) it's plain divide and conquer and caching buys nothing. The signal is exponentially many calls over polynomially many distinct inputs.

The four stages

Every DP solution is the same four stages
Every DP solution is the same four stages

Write the recursion first — it's obviously correct. Then memoise, tabulate, and roll. Each step is mechanical once the recurrence exists.

The combiner is chosen by the question

The structure comes from the allowed moves; the combiner comes from what's being asked. They're independent choices.

GoalCombinerQuestions
count the ways+1, 7
best / cheapestmin / max2, 3, 4, 8, 9, 11, 12
does one exist||10, 12

Min Cost Climbing Stairs is Climbing Stairs with min for + — same recurrence, one substitution.

When you CAN roll to O(1) space

Only when the recurrence reads a fixed window of previous cells.

RecurrenceRolls?Questions
dp[i] = f(dp[i-1], dp[i-2])yes — two variables1, 2, 3, 4, 7, 9
dp[i] = f(dp[j]) for a range of jno — array required8, 10, 11, 12

Word Break, Coin Change and LIS are the counterexamples: each reads many earlier cells, so nothing can be discarded.

Ascending vs descending — knapsack's whole distinction

Loop directionMeaningQuestion
ascendingan item may be reused — unbounded knapsack8 (Coin Change)
descendingeach item used once — 0/1 knapsack12 (Partition Equal Subset Sum)

Descending means dp[s - n] still holds the value from before this item was considered. Verified: the ascending variant of question 12 disagreed with the correct answer on 44 of 400 random inputs.

Where DP is NOT the best answer

Question 5. The isPal[i][j] table is O(n²) time and space; expand-around-centre is O(n²) time and O(1) space. The table earns its place only when queried repeatedly — which is Palindrome Partitioning, not this.

LIS: the reframe that changes what's indexed

tails holds the smallest tail for each length
tails holds the smallest tail for each length

The O(n²) DP indexes by position. The patience method indexes by lengthtails[k] is the smallest tail of any increasing subsequence of length k+1. That array is sorted by construction, so the scan becomes a binary search.

tails is not the answer subsequence. On the example it ends as [2,3,7,18], but 18 comes after 101 in the input. Only its length is meaningful.

The traps

TrapSymptom
memo[n] != 0 when 0 is a valid answer (Q1)Infinite recomputation
Returning dp[n-1] instead of dp[n] (Q2)Answers a different question
dp[1] = nums[1] instead of max (Q3)[5,1] returns 1
Forgetting the circular case split (Q4)[5,1,1,5] returns 10 instead of 6
Only odd-length centres (Q5, Q6)"cbbd" misses "bb"
Missing the leading-zero guard (Q7)"06" returns 1 instead of 0
two <= 26 without >= 10 (Q7)"06" counted as F
Integer.MAX_VALUE sentinel (Q8)Overflows to MIN_VALUE; wins every min
Greedy coin selection (Q8)[1,3,4] amount 6 gives 3, not 2
Not snapshotting the old max (Q9)[-1,-2,-3] returns 12 — an impossible value
boolean[] memo when false is an answer (Q10)Recomputes every false
dp[n-1] instead of max(dp) (Q11)[1,2,3,0] returns 1
Missing the odd-total check (Q12)[1,2,3,5] returns true
Ascending loop in 0/1 knapsack (Q12)Items reused

Sentinels need a domain check

Three questions overload a value to mean "not computed" or "unreachable", and each works only because the sentinel is provably outside the answer range:

QuestionSentinelSafe because
Climbing Stairs0The count is always >= 1
Coin Changeamount + 1Worst case is amount coins — and it survives + 1
Word Breaknull (Boolean[])false is a legitimate answer

Integer.MAX_VALUE fails the Coin Change case precisely because the recurrence adds to it.

Verification

Every snippet compiled and cross-checked against an independent implementation — 4,000+ randomized cases:

Five deliberately-broken variants confirm the traps: