DP Examples — Full Explanation

Revision en5, by Nourhan_Abo-Heba, 2026-02-23 17:20:48

1. Fibonacci

Idea: Each number is the sum of the two before it.

0, 1, 1, 2, 3, 5, 8, 13, ...
fib[1] = 0, fib[2] = 1;
for (int i = 3; i <= n; i++) fib[i] = fib[i-1] + fib[i-2];

Trace for n=6:

fib[1] = 0
fib[2] = 1
fib[3] = fib[2] + fib[1] = 1+0 = 1
fib[4] = fib[3] + fib[2] = 1+1 = 2
fib[5] = fib[4] + fib[3] = 2+1 = 3
fib[6] = fib[5] + fib[4] = 3+2 = 5

Why DP? You reuse previously computed values instead of recalculating recursively every time.


2. Number of Ways (Staircase)

Problem: You're on step s, want to reach step e. Each move you can jump +1, +2, or +3 steps. How many ways?

dp[s] = 1;  // one way to be at start
for (int i = s+1; i <= e; i++) {
    if (i-1 >= s) dp[i] += dp[i-1];  // came from 1 step back
    if (i-2 >= s) dp[i] += dp[i-2];  // came from 2 steps back
    if (i-3 >= s) dp[i] += dp[i-3];  // came from 3 steps back
}

Trace for s=0, e=4:

dp[0] = 1
dp[1] = dp[0]           = 1
dp[2] = dp[1]+dp[0]     = 2
dp[3] = dp[2]+dp[1]+dp[0] = 4
dp[4] = dp[3]+dp[2]+dp[1] = 7

Intuition: To know how many ways to reach step i, just ask: how many ways were there to reach the 3 steps before it? Add them up.


3. Broken Steps

Problem: Same as above, but some steps are broken — you can't land on them.

if (broken[i]) continue;         // skip broken steps entirely
if (!broken[i-1] ...) dp[i] += dp[i-1];  // only add if that step is usable

Trace for s=0, e=4, broken={2}:

dp[0] = 1
dp[1] = dp[0] = 1
dp[2] → SKIP (broken)
dp[3] = dp[2](broken, skip) + dp[1] + dp[0] = 0+1+1 = 2
dp[4] = dp[3] + dp[2](broken=0) + dp[1] = 2+0+1 = 3

Key insight: A broken step contributes 0 ways because dp[broken] = 0 and we skip computing it. But we still need to avoid adding FROM broken steps, hence the !broken[i-k] checks.


4. LIS — Longest Increasing Subsequence

Problem: Given an array, find the length of the longest subsequence where each element is strictly greater than the previous.

Example:

[3, 1, 4, 1, 5, 9, 2, 6] → LIS is [1, 4, 5, 9] = length 4
dp[i] = 1;  // every element alone is a subsequence of length 1
for (int i = 0; i < n; i++)
    for (int o = 0; o < i; o++)
        if (s[o] < s[i])
            dp[i] = max(dp[i], dp[o] + 1);

Trace for [3, 1, 4, 2, 5]:

i=0: s[0]=3, dp[0]=1         → [3]
i=1: s[1]=1, nothing before is <1, dp[1]=1   → [1]
i=2: s[2]=4, s[0]=3<4 → dp[2]=dp[0]+1=2
              s[1]=1<4 → dp[2]=max(2,dp[1]+1)=2  → [1,4] or [3,4]
i=3: s[3]=2, s[1]=1<2 → dp[3]=dp[1]+1=2     → [1,2]
i=4: s[4]=5, s[0]=3<5 → dp[4]=2
              s[2]=4<5 → dp[4]=max(2,dp[2]+1)=3
              s[3]=2<5 → dp[4]=max(3,dp[3]+1)=3  → [1,4,5] or [1,2,5]

ans = max(1,1,2,2,3) = 3

Intuition: dp[i] = "what's the longest increasing subsequence that ends exactly at position i?" For each i, look back at all previous elements smaller than s[i] and extend the best one.

Link: https://cses.fi/problemset/task/1145


5. Grid Max Path

Problem: Given an n×m grid with values, start at top-left (1,1), reach bottom-right (n,m). You can only move right or down. Maximize the sum of values collected.

dp[1][1] = s[1][1];
// first column: can only come from above
for (int i = 2; i <= n; i++) dp[i][1] = dp[i-1][1] + s[i][1];
// first row: can only come from left
for (int i = 2; i <= m; i++) dp[1][i] = dp[1][i-1] + s[1][i];
// rest: come from above or left, pick better
for (int i = 2; i <= n; i++)
    for (int o = 2; o <= m; o++)
        dp[i][o] = max(dp[i-1][o], dp[i][o-1]) + s[i][o];

Trace for grid:

Grid:          dp table:
1  3  2        1   4   6
4  2  1  →     5   7   8
2  1  3        7   8  11

Step by step:

dp[1][1]=1, dp[1][2]=4, dp[1][3]=6
dp[2][1]=5, dp[3][1]=7
dp[2][2]=max(dp[1][2], dp[2][1])+2 = max(4,5)+2 = 7
dp[2][3]=max(dp[1][3], dp[2][2])+1 = max(6,7)+1 = 8
dp[3][2]=max(dp[2][2], dp[3][1])+1 = max(7,7)+1 = 8
dp[3][3]=max(dp[2][3], dp[3][2])+3 = max(8,8)+3 = 11

Intuition: Each cell can only be reached from the top or the left. Pick whichever path brought the higher sum, then add the current cell's value.

Link: https://cses.fi/problemset/task/1638 (or grid path variant)


6. Dice Combinations

Problem: How many ways can you make sum n by rolling a 6-sided die any number of times? Order matters — (1,2) and (2,1) are different.

dp[0] = 1;  // one way to make sum 0: roll nothing
for (int i = 1; i <= n; i++)
    for (int o = 1; o <= 6; o++)
        if (i - o >= 0)
            dp[i] += dp[i-o];   // mod 1e9+7

Trace for n=4:

dp[0] = 1
dp[1] = dp[0]                         = 1
dp[2] = dp[1]+dp[0]                   = 2
dp[3] = dp[2]+dp[1]+dp[0]             = 4
dp[4] = dp[3]+dp[2]+dp[1]+dp[0]       = 8

Intuition: To make sum i, the last die roll was some face o (1–6). Before that roll, the sum was i-o. So count all ways to make i-o and add them up. dp[0]=1 is the base — "empty sequence" has exactly one way.

Why mod 1e9+7? The numbers grow astronomically fast. Modulo keeps them from overflowing int.

Link: https://cses.fi/problemset/task/1633


The Universal DP Thought Process

Every DP problem follows the same 3 steps:

1. DEFINE  → what does dp[i] mean?
2. TRANSITION → how do I compute dp[i] from smaller values?
3. BASE CASE → what's the starting value I know for sure?
Problem dp[i] means Transition
Fibonacci i-th fib number dp[i-1] + dp[i-2]
Staircase ways to reach step i sum of 3 previous
LIS LIS ending at index i max(dp[o]+1) for s[o]<s[i]
Grid best sum to reach cell (i,j) max(top, left) + val
Dice ways to make sum i sum over 6 dice faces

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en8 English Nourhan_Abo-Heba 2026-03-02 22:41:44 51
en7 English Nourhan_Abo-Heba 2026-03-02 22:40:50 2119
en6 English Nourhan_Abo-Heba 2026-03-02 22:37:01 1929
en5 English Nourhan_Abo-Heba 2026-02-23 17:20:48 154
en4 English Nourhan_Abo-Heba 2026-02-23 17:14:29 148
en3 English Nourhan_Abo-Heba 2026-02-23 17:13:10 809
en2 English Nourhan_Abo-Heba 2026-02-23 17:01:57 1609
en1 English Nourhan_Abo-Heba 2026-02-23 16:56:24 5967 Initial revision (published)