Overview
1D dynamic programming solves problems where the state is a single index or value: dp[i] is the answer for the first i elements, or for a target amount i. Each dp[i] is computed from a few earlier entries by a transition rule, and the final answer is dp[n] or the best over all dp[i].
Most 1D DP problems are variations of a handful of shapes: 'take or skip' (House Robber), 'last step' (Climbing Stairs), 'best ending here' (Kadane, LIS), and 'split the prefix' (Word Break, Decode Ways). Learning to recognize the shape is most of the work.
Your best plan for reaching day i depends only on your best plans for a few previous days plus today's choice. Once you know the best result for each earlier day, today's best result takes a moment to compute.
When to use it
- Input is a single sequence or a single number (amount, n).
- The problem asks for a count of ways, a minimum cost, a maximum value, or feasibility.
- Choices at each index affect only nearby earlier indexes.
- A brute-force recursion f(i) calls f(i - 1), f(i - 2), ... repeatedly.
Problem patterns it solves
Recognize it when: cannot take adjacent items: dp[i] = max(dp[i - 1], dp[i - 2] + value[i]).
- 198. House Robber
- 213. House Robber II
- 740. Delete and Earn
Recognize it when: number of ways to reach n with allowed steps.
- 70. Climbing Stairs
- 91. Decode Ways
- 1137. N-th Tribonacci Number
Recognize it when: cheapest way to get to the end.
- 746. Min Cost Climbing Stairs
- 983. Minimum Cost For Tickets
- 1025. Divisor Game
Recognize it when: can s[0..i) be broken into valid pieces.
- 139. Word Break
- 132. Palindrome Partitioning II
- 1043. Partition Array for Maximum Sum
Recognize it when: longest or largest subsequence or subarray ending at each index.
- 53. Maximum Subarray
- 300. Longest Increasing Subsequence
- 152. Maximum Product Subarray
Recognize it when: can you reach the last index; minimum jumps.
- 55. Jump Game
- 45. Jump Game II
- 1306. Jump Game III
Where it is used in real software
TeX breaks paragraphs into lines by minimizing a 'badness' cost over all break points, a 1D DP over word positions.
Splitting text without spaces (such as Chinese or hashtags) into dictionary words is word-break DP.
Choosing non-conflicting jobs or ad slots to maximize revenue (weighted interval scheduling) is dp over sorted jobs.
Choosing the cheapest combination of daily, weekly, and monthly passes is the Minimum Cost For Tickets problem.
Key terms
- dp[i]
- The answer for the subproblem ending at or containing index i.
- Transition
- Formula for dp[i] in terms of earlier states.
- Base case
- dp[0] (and sometimes dp[1]) known directly.
- Rolling variables
- Keep only the last few dp values to use O(1) space.
Five-step DP method
- 1Define the state
dp[i] = maximum money robbing houses 0..i (write it as a sentence).
- 2Find the transition
Either skip house i (dp[i - 1]) or rob it (dp[i - 2] + nums[i]); take the max.
- 3Set base cases
dp[0] = nums[0], dp[1] = max(nums[0], nums[1]).
- 4Choose the order
Increasing i, so dependencies are already computed.
- 5Optimize space
Only dp[i - 1] and dp[i - 2] are read: keep two variables.
STEP 1dp[0] = 2: rob the only house.
Decode Ways for "226"
'1' to '26' map to letters; dp[i] = ways to decode the first i characters
| i | One digit s[i-1] | Two digits s[i-2..i-1] | dp[i] |
|---|---|---|---|
| 0 | - | - | 1 (empty string) |
| 1 | '2' valid: + dp[0] | - | 1 |
| 2 | '2' valid: + dp[1] = 1 | '22' valid: + dp[0] = 1 | 2 |
| 3 | '6' valid: + dp[2] = 2 | '26' valid: + dp[1] = 1 | 3 |
NOWi: 0 | One digit s[i-1]: - | Two digits s[i-2..i-1]: - | dp[i]: 1 (empty string)
3 decodings: BBF (2, 2, 6), BZ (2, 26), VF (22, 6). A '0' can never stand alone, and two-digit values must be between 10 and 26.
Implementation
function rob(nums) { let prev2 = 0, prev1 = 0; // dp[i - 2], dp[i - 1] for (const x of nums) { const current = Math.max(prev1, prev2 + x); prev2 = prev1; prev1 = current; } return prev1;} // 213. House Robber II: circle, so exclude either the first or the last housefunction robCircle(nums) { if (nums.length === 1) return nums[0]; return Math.max(rob(nums.slice(1)), rob(nums.slice(0, -1)));} function numDecodings(s) { const dp = new Array(s.length + 1).fill(0); dp[0] = 1; for (let i = 1; i <= s.length; i++) { if (s[i - 1] !== "0") dp[i] += dp[i - 1]; const two = Number(s.slice(i - 2, i)); if (i >= 2 && two >= 10 && two <= 26) dp[i] += dp[i - 2]; } return dp[s.length];} function wordBreak(s, wordDict) { const words = new Set(wordDict); const dp = new Array(s.length + 1).fill(false); dp[0] = true; for (let i = 1; i <= s.length; i++) { for (let j = 0; j < i; j++) { if (dp[j] && words.has(s.slice(j, i))) { dp[i] = true; break; } } } return dp[s.length];}Complexity and performance
Constant work per state.
Try every split point j < i.
Rolling variables when only a few states are needed.
Trade-offs
The array is easier to debug and needed if you reconstruct choices; rolling variables save memory.
Some 1D problems (Jump Game, Jump Game II) have greedy solutions that are simpler; DP is the safe default when greedy is unproven.
Variants and related techniques
Break the circle by solving twice: without the first element and without the last.
Delete and Earn transforms to House Robber over values instead of indexes.
Keep a choice[] array or walk back through dp to recover which items were chosen.
Common mistakes
- Off-by-one between dp indexes and input indexes.
Fix: When dp has n + 1 entries, dp[i] usually refers to the first i elements, so the current element is s[i - 1].
- Wrong base case for counting problems.
Fix: The empty prefix usually has exactly 1 way.
- Leading zeros in Decode Ways.
Fix: '0' alone is invalid; '06' is not a valid two-digit code.
Interview questions
How do you find the recurrence for a 1D DP problem?
Think about the last decision: what happened at index i? Each possible last decision leads to a smaller subproblem; combine them with max, min, or sum depending on the question.
When can you reduce O(n) space to O(1)?
When dp[i] depends only on a fixed number of previous states, such as dp[i - 1] and dp[i - 2].
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 70. Climbing Stairs | Easy | Last step. |
| 198. House Robber | Medium | Take or skip. |
| 213. House Robber II | Medium | Break the circle. |
| 91. Decode Ways | Medium | One or two characters. |
| 139. Word Break | Medium | Split the prefix. |
| 983. Minimum Cost For Tickets | Medium | Several look-backs. |