Free beta: 30 days of full access, no card needed.Sign up free

We use necessary cookies to run the site (sign-in and language). The contact and bug-report forms also use Google reCAPTCHA for spam protection, loaded only if you accept. Privacy policy

Field manual

Dynamic programming (1-D)

O(states)

Fill a row of boxes dp[i], where each box is worked out from a couple of earlier boxes by one rule, the recurrence. Scanning left to right once turns an exponential brute force into a single O(n) pass.

Signals

best/maximum/minimum ending at position iways to reach, climb, or tilecannot pick two adjacent itemsminimum cost path through a gridoverlapping subproblems, same state recomputed

Template

function maxSubarraySum(nums) {
    let best = nums[0];
    let curr = nums[0];
    for (let i = 1; i < nums.length; i++) {
        curr = Math.max(nums[i], curr + nums[i]);
        best = Math.max(best, curr);
    }
    return best;
}

Looks similar, isn't

  • Greedy: Greedy locks in whichever choice looks best right now and never revisits it, which only works when you can prove that local choice is always safe with an exchange argument. Most counting or optimization problems with a state that overlaps across choices need the full dp table instead, because the locally best pick can block a better global answer.
  • Memoization: Memoization is the same recurrence written top-down: a plain recursive function plus a cache for states it has already solved. Bottom-up DP fills the table in a fixed order instead, which avoids recursion depth limits and lets you drop old rows once nothing later needs them.

n up to 1e5..1e6, one left-to-right pass filling dp[i] from a couple earlier boxes -> O(n): each state computed once in O(1).

Learn this pattern