Python Algorithms & Data Structures track

Lesson 20 · Part 6: DP & Greedy

DP Foundations

Memoisation and tabulation with Fibonacci and staircase problems

Part 6: DP & Greedy3 min readFree lesson

Introduction

Dynamic Programming (DP) is an algorithm design technique that breaks complex problems into smaller subproblems and stores (memoizes) already computed results to avoid redundant calculations.

Two conditions where DP can be applied:

  1. Overlapping Subproblems: the same subproblem occurs repeatedly
  2. Optimal Substructure: the overall optimal solution is composed of optimal solutions to subproblems
Simple Recursion vs DP — Fibonacci Example

Simple Recursion fib(5):
    fib(5)
   /      \
fib(4)   fib(3)    ← fib(3) duplicated!
 /   \   /   \
fib(3) fib(2) fib(2) fib(1)   ← fib(2) duplicated 3 times!
...
→ Time: O(2^n), Space: O(n)

DP Memoization fib(5):
    fib(5)
   /      \
fib(4)   [Cache: 3]  ← Already calculated
 /   \
fib(3) [Cache: 2]
...
→ Time: O(n), Space: O(n)

Learning Goals

  1. Understand the difference between memoization (top-down) and tabulation (bottom-up) and be able to implement each.
  2. Systematically apply the three steps of a DP solution (define states → recurrence relation → initial values).
  3. Solve basic DP problems like climbing stairs and coin change by defining states and recurrence relations on your own.

Key Concepts

3 Steps of DP Design

1. State Definition:
dp[i] = "Considering up to the i-th ..."
dp[i][j] = "Considering up to the i-th and j-th ..."

2. Recurrence Relation:
   dp[i] = f(dp[i-1], dp[i-2], ...)

3. Base Case:
   dp[0] = ..., dp[1] = ...

Memoization vs Tabulation

┌──────────────────┬────────────────────────┬──────────────────────────────┐
│ Feature           │ Memoization (Top-down) │ Tabulation (Bottom-up)       │
├──────────────────┼────────────────────────┼──────────────────────────────┤
│ Direction         │ Big problem → Small problem │ Small problem → Big problem │
│ Implementation    │ Recursion + Cache       │ Loop + Table                 │
│ Required Computation │ Only what is actually needed │ Compute all subproblems │
│ Space             │ O(n) + Recursion stack  │ O(n) (no stack)             │
│ Speed             │ Function call overhead  │ Usually faster              │
│ Implementation Difficulty │ Intuitive            │ Order needs consideration   │
└──────────────────┴────────────────────────┴──────────────────────────────┘

Fibonacci Complexity Comparison

┌──────────────────────┬───────────┬──────────┐
│ Method               │ Time       │ Space     │
├──────────────────────┼───────────┼──────────┤
│ Simple Recursion     │ O(2^n)     │ O(n)      │
│ Memoization          │ O(n)       │ O(n)      │
│ Tabulation           │ O(n)       │ O(n)      │
│ Space Optimization   │ O(n)       │ O(1)      │
└──────────────────────┴───────────┴──────────┘

Stair Climbing Recurrence Relation

dp[i] = The number of ways to climb to the i-th stair (1 step or 2 steps at a time)

dp[1] = 1   (1-step: {1})
dp[2] = 2   (2-steps: {1,1}, {2})
dp[i] = dp[i-1] + dp[i-2]   (either came 1 step or 2 steps at the end)

→ Same recurrence relation as Fibonacci!

Coin Change (Reason DP is Needed)

Coins: [1, 5, 6, 9], Change: 11

Greedy: 9 + 1 + 1 = 3 coins
DP: 5 + 6 = 2 coins  ← Optimal!

Reason greedy fails: local optimum ≠ global optimum

DP recurrence formula:
dp[i] = minimum number of coins needed to make i won
dp[0] = 0
dp[i] = min(dp[i - coin] + 1) for coin in coins if coin ≤ i

Example

File Contents
src/01_fibonacci_memo.py Fibonacci: Simple/Memoization/part@lru_cache
src/02_fibonacci_tab.py Fibonacci: Tabulation, Space O(1)
src/03_climbing_stairs.py Stair Climbing, Variant Problems
src/04_coin_change.py Minimum Number of Coins, Number of Ways

Common Mistakes

  1. Undefined State: You cannot set up a recurrence relation without first clearly defining what dp[i] means.
  2. Missing Initial Values (Base Case): If you omit or incorrectly set dp[0], dp[1], all calculations will be wrong.
  3. Incorrect Recurrence Direction: In bottom-up, when calculating dp[i], ensure that the required dp[j] (j < i) has already been computed.
  4. Array Index Out of Range: When handling i - coin < 0 in dp[i - coin], omissions can occur.
  5. Confusing with Greedy: DP is needed when "always choosing the largest" is not optimal — always check examples where greedy fails.

Summary

  • DP = memoization + optimal substructure + overlapping subproblems
  • Top-down (recursion + cache) vs Bottom-up (iteration + table)
  • 3-step design: define state → recurrence relation → initial values
  • Space optimization: If only the previous two are needed, can reduce to O(1)

Try It Yourself

  1. Get the nth Fibonacci number using matrix exponentiation in O(log n)
  2. Longest Increasing Subsequence (LIS) — prepare for the next lesson
  3. Grid DP: number of paths from top-left to bottom-right in an m×n grid

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_fibonacci_memo.pyPython
#Topic: Fibonacci — Simple Recursion, Memoization, @lru_cache
#Time: O(2^n) simple recursion, O(n) memoization
#Space: O(n) common

import sys
import time
from functools import lru_cache

sys.setrecursionlimit(10_000)


#── Method 1: Simple Recursion (Inefficient) ────────────────────────────────────────────
def fib_naive(n: int) -> int:
    """Simple recursive Fibonacci.

    Time:  O(2^n) — Exponential redundant calculations
    Space: O(n)   — Recursion stack"""
    if n <= 1:
        return n
    return fib_naive(n - 1) + fib_naive(n - 2)


#── Method 2: Manual Memoization ────────────────────────────────────────────
def fib_memo(n: int, cache: dict[int, int] | None = None) -> int:
    """Memoized Fibonacci.

    Time:  O(n) — Each n is calculated only once
    Space: O(n) — Cache dictionary"""
    if cache is None:
        cache = {}
    if n in cache:
        return cache[n]
    if n <= 1:
        return n
    cache[n] = fib_memo(n - 1, cache) + fib_memo(n - 2, cache)
    return cache[n]


#── Method 3: @lru_cache Decorator ─────────────────────────────────────────
@lru_cache(maxsize=None)
def fib_lru(n: int) -> int:
    """Automatic memoization using @lru_cache.

    Time:  O(n)
    Space: O(n)"""
    if n <= 1:
        return n
    return fib_lru(n - 1) + fib_lru(n - 2)


#── Method 4: Explicit Memo Table (Class Variable) ─────────────────────────────
_memo: dict[int, int] = {}


def fib_global_memo(n: int) -> int:
    """Global dictionary memoization.

    Time:  O(n) — Across all calls
    Space: O(n)"""
    if n in _memo:
        return _memo[n]
    if n <= 1:
        _memo[n] = n
        return n
    _memo[n] = fib_global_memo(n - 1) + fib_global_memo(n - 2)
    return _memo[n]


#── Performance Comparison ─────────────────────────────────────────────────────────
def benchmark(func, n: int, label: str) -> None:
    start = time.perf_counter()
    result = func(n)
    elapsed = time.perf_counter() - start
    print(f"{label:30s}: fib({n}) = {result}, Time = {elapsed:.6f}s")


if __name__ == "__main__":
    print("=== Fibonacci Result Check ===")
    for i in range(11):
        print(f"  fib({i:2d}) = {fib_lru(i)}", end="")
        if i < 10:
            print(",", end="")
    print()

    print("=== Performance Comparison ===")
    n = 30
    benchmark(fib_naive, n, "Simple Recursion")
    benchmark(lambda x: fib_memo(x), n, "Manual Memoization")
    benchmark(fib_lru, n, "@lru_cache")
    benchmark(fib_global_memo, n, "Global Dictionary")

    print("=== Limits of Simple Recursion ===")
    print(f"fib(35) simple recursion: {fib_naive(35)} (slow...)")
    print(f"fib(35) lru_cache:  {fib_lru(35)} (instant)")

    print("=== @lru_cache Cache Information ===")
    info = fib_lru.cache_info()
    print(f"  hits={info.hits}, misses={info.misses}, size={info.currsize}")
src/02_fibonacci_tab.pyPython
#Topic: Fibonacci Tabulation (Bottom-up) and Space Optimization
#Time: O(n)   Space: O(n) → O(1) Optimization

def fib_tabulation(n: int) -> int:
    """Tabulation Fibonacci (Bottom-up).

    Fill the table in order from the smallest problem.

    Time:  O(n)
    Space: O(n) — dp array"""
    if n <= 1:
        return n

    dp = [0] * (n + 1)
    dp[0] = 0
    dp[1] = 1

    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]

    return dp[n]


def fib_space_optimized(n: int) -> int:
    """Space-optimized Fibonacci.

    dp[i] only needs dp[i-1] and dp[i-2] → 2 variables are enough

    Time:  O(n)
    Space: O(1) ← key!"""
    if n <= 1:
        return n

    prev2, prev1 = 0, 1
    for _ in range(2, n + 1):
        curr = prev1 + prev2
        prev2 = prev1
        prev1 = curr

    return prev1


def fib_generator(count: int):
    """Fibonacci sequence generator.

    Time:  O(1) per yield
    Space: O(1)"""
    a, b = 0, 1
    for _ in range(count):
        yield a
        a, b = b, a + b


def fib_matrix(n: int) -> int:
    """Fibonacci using matrix exponentiation O(log n).

    [[1,1],[1,0]]^n = [[fib(n+1), fib(n)], [fib(n), fib(n-1)]]

    Time:  O(log n)
    Space: O(log n) — recursion stack"""
    if n <= 1:
        return n

    def mat_mul(a: list[list[int]], b: list[list[int]]) -> list[list[int]]:
        return [
            [a[0][0]*b[0][0] + a[0][1]*b[1][0],
             a[0][0]*b[0][1] + a[0][1]*b[1][1]],
            [a[1][0]*b[0][0] + a[1][1]*b[1][0],
             a[1][0]*b[0][1] + a[1][1]*b[1][1]],
        ]

    def mat_pow(m: list[list[int]], p: int) -> list[list[int]]:
        if p == 1:
            return m
        if p % 2 == 0:
            half = mat_pow(m, p // 2)
            return mat_mul(half, half)
        return mat_mul(m, mat_pow(m, p - 1))

    base = [[1, 1], [1, 0]]
    result = mat_pow(base, n)
    return result[0][1]


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("=== Tabulation vs Space Optimization ===")
    print(f"{'n':>5} | {'Tabulation':>15} | {'Space O(1)':>15} | {'Matrix O(logn)':>15}")
    print("-" * 58)
    for n in [0, 1, 5, 10, 20, 30, 40]:
        t = fib_tabulation(n)
        s = fib_space_optimized(n)
        m = fib_matrix(n)
        print(f"{n:>5} | {t:>15} | {s:>15} | {m:>15}")

    print("\n=== Tabulation Table Visualization (n=10) ===")
    dp = [0] * 11
    dp[1] = 1
    print(f"dp[0] = {dp[0]}")
    print(f"dp[1] = {dp[1]}")
    for i in range(2, 11):
        dp[i] = dp[i-1] + dp[i-2]
        print(f"dp[{i}] = dp[{i-1}] + dp[{i-2}] = {dp[i-1]} + {dp[i-2]} = {dp[i]}")

    print("\n=== Fibonacci Generator (First 15) ===")
    fib_list = list(fib_generator(15))
    print(f"  {fib_list}")

    print("\n=== Space Optimization Process Visualization ===")
    prev2, prev1 = 0, 1
    print(f"Initial: prev2={prev2}, prev1={prev1}")
    for i in range(2, 11):
        curr = prev1 + prev2
        print(f"  i={i}: {prev2} + {prev1} = {curr}")
        prev2, prev1 = prev1, curr
    print(f"Result: {prev1}")
src/03_climbing_stairs.pyPython
#Topic: Climbing Stairs DP
# Time: O(n)   Space: O(n) → O(1)

def climb_basic(n: int) -> int:
    """Basic Stair Climbing: 1 or 2 steps at a time.

    dp[i] = number of ways to reach step i
    dp[i] = dp[i-1] + dp[i-2]

    Time:  O(n)
    Space: O(n)"""
    if n <= 2:
        return n

    dp = [0] * (n + 1)
    dp[1] = 1   # {1}
    dp[2] = 2   # {1,1}, {2}

    for i in range(3, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]

    return dp[n]


def climb_k_steps(n: int, k: int) -> int:
    """Variation: When you can climb up to k steps at a time.

    dp[i] = dp[i-1] + dp[i-2] + ... + dp[i-k]

    Time:  O(n × k)
    Space: O(n)"""
    dp = [0] * (n + 1)
    dp[0] = 1   #Step 0 = Starting point

    for i in range(1, n + 1):
        for step in range(1, k + 1):
            if i - step >= 0:
                dp[i] += dp[i - step]

    return dp[n]


def climb_no_consecutive_two(n: int) -> int:
    """Baekjoon 2579 Variation: When you cannot climb 2 steps consecutively twice in a row.

    State: dp[i][j]
      i = current stair number
      j = 0: this stair is the first in a consecutive sequence, j = 1: this stair is the second in a consecutive sequence

    Actual problem 2579 does not allow 3 consecutive steps, so refer to the solution below:

    dp[i][0] = stepping on stair i only once consecutively (jump 2 steps from i-2)
    dp[i][1] = stepping on stair i twice consecutively (jump 1 step from i-1)
    dp[i][0] = dp[i-2][0] + dp[i-2][1]  (including score)
    dp[i][1] = dp[i-1][0]               (including score)

    Time:  O(n)
    Space: O(n)"""
    if n == 0:
        return 0
    if n == 1:
        return 1
    if n == 2:
        return 2

    #Simple stair count (score-less version)
    dp0 = [0] * (n + 1)  #First in a row
    dp1 = [0] * (n + 1)  #Second in a row

    dp0[1] = 1
    dp1[1] = 0
    if n >= 2:
        dp0[2] = 1
        dp1[2] = 1

    for i in range(3, n + 1):
        dp0[i] = dp0[i - 2] + dp1[i - 2]
        dp1[i] = dp0[i - 1]

    return dp0[n] + dp1[n]


def climb_ways_space_optimized(n: int) -> int:
    """Space-optimized stair climbing.

    Time:  O(n)
    Space: O(1)"""
    if n <= 2:
        return n
    a, b = 1, 2
    for _ in range(3, n + 1):
        a, b = b, a + b
    return b


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("=== Basic Stair Climbing (1 step or 2 steps) ===")
    print(f"{'n':>5} | {'Number of ways':>10}")
    print("-" * 20)
    for n in range(1, 11):
        print(f"{n:>5} | {climb_basic(n):>10}")

    print("=== Climbing up to k steps ===")
    n = 10
    for k in [1, 2, 3, 4]:
        print(f"n={n}, k={k}: {climb_k_steps(n, k)} ways")

    print("=== Case enumeration (n=4, 1 step or 2 steps) ===")
    def enumerate_ways(n: int) -> list[list[int]]:
        if n == 0:
            return [[]]
        if n < 0:
            return []
        ways = []
        for step in [1, 2]:
            for rest in enumerate_ways(n - step):
                ways.append([step] + rest)
        return ways

    for n in range(1, 6):
        ways = enumerate_ways(n)
        print(f"n={n}: {len(ways)} ways → {ways[:3]}{'...' if len(ways) > 3 else ''}")

    print("=== Space Optimization ===")
    for n in [5, 10, 20, 50]:
        print(f"  climb({n}) = {climb_ways_space_optimized(n)}")
src/04_coin_change.pyPython
#Topic: Coin Change — Minimum Number (Greedy Failure), Number of Cases
# Time: O(amount × len(coins))
# Space: O(amount)


def min_coins(coins: list[int], amount: int) -> int:
    """Minimum Coin Count (DP).

    dp[i] = minimum number of coins to make i won
    dp[0] = 0
    dp[i] = min(dp[i - coin] + 1) for coin ≤ i

    Time:  O(amount × len(coins))
    Space: O(amount)"""
    INF = float("inf")
    dp = [INF] * (amount + 1)
    dp[0] = 0

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1

    return int(dp[amount]) if dp[amount] != INF else -1


def greedy_coins(coins: list[int], amount: int) -> int:
    """Greedy Coin Change (not always optimal!).

    Time:  O(len(coins) × log(len(coins))) — including sorting
    Space: O(1)"""
    coins_sorted = sorted(coins, reverse=True)
    count = 0
    remaining = amount
    for coin in coins_sorted:
        count += remaining // coin
        remaining %= coin
    return count if remaining == 0 else -1


def count_ways(coins: list[int], amount: int) -> int:
    """Number of ways to make amount with coins (order doesn't matter).

    dp[i] = number of ways to make i won
    Process each coin in order → avoids duplicate permutations

    Time:  O(amount × len(coins))
    Space: O(amount)"""
    dp = [0] * (amount + 1)
    dp[0] = 1   #0 Won: 1 way without using coins

    for coin in coins:           #External: Coin types
        for i in range(coin, amount + 1):   #Internal: Amount
            dp[i] += dp[i - coin]

    return dp[amount]


def count_permutations(coins: list[int], amount: int) -> int:
    """Number of permutations to make amount with coins (order matters).

    dp[i] = number of permutations to make i won
    (outer: amount, inner: coin types)

    Time:  O(amount × len(coins))
    Space: O(amount)"""
    dp = [0] * (amount + 1)
    dp[0] = 1

    for i in range(1, amount + 1):   #External: Amount
        for coin in coins:            #Internal: Coin types
            if coin <= i:
                dp[i] += dp[i - coin]

    return dp[amount]


def reconstruct_coins(coins: list[int], amount: int) -> list[int] | None:
    """Recovering minimum coin combination.

    Time:  O(amount × len(coins))
    Space: O(amount)"""
    INF = float("inf")
    dp = [INF] * (amount + 1)
    dp[0] = 0
    parent = [-1] * (amount + 1)   #For Backtracking

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1
                parent[i] = coin

    if dp[amount] == INF:
        return None

    #Backtracking
    result: list[int] = []
    curr = amount
    while curr > 0:
        coin = parent[curr]
        result.append(coin)
        curr -= coin

    return sorted(result, reverse=True)


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("=== Greedy Failure Example ===")
    coins_bad = [1, 5, 6, 9]
    amount = 11
    greedy_ans = greedy_coins(coins_bad, amount)
    dp_ans = min_coins(coins_bad, amount)
    print(f"Coins: {coins_bad}, Amount: {amount}")
    print(f"Greedy: {greedy_ans} coins (9+1+1)")
    print(f"DP:     {dp_ans} coins (5+6)")

    print("=== Korean Coins (Greedy Optimal) ===")
    korean_coins = [500, 100, 50, 10, 5, 1]
    for amount in [1260, 730, 999]:
        g = greedy_coins(korean_coins, amount)
        d = min_coins(korean_coins, amount)
        print(f"{amount} Won: Greedy={g}, DP={d}, {'Match' if g == d else 'Mismatch!'}")

    print("=== Number of Cases (Combination vs Permutation) ===")
    coins = [1, 2, 3]
    for amt in [3, 4, 5]:
        comb = count_ways(coins, amt)
        perm = count_permutations(coins, amt)
        print(f"{amt} Won: Combination={comb}, Permutation={perm}")

    print("=== Minimum Coin Combination Recovery ===")
    test_cases = [(coins_bad, 11), ([1, 2, 5], 11), ([2], 3)]
    for c, a in test_cases:
        combo = reconstruct_coins(c, a)
        print(f"Coins={c}, Amount={a}: {combo}")

Assignment

Problem 1: Baekjoon 2579 — Climbing Stairs (Silver III)

Link: https://www.acmicpc.net/problem/2579

Problem Summary

Given that each stair has a score, find the maximum score. Rules: You cannot step on three consecutive stairs, and the last stair must be stepped on.

Recurrence Relation

dp[i][0] = The maximum when stepping on the i-th alone (jumped from i-2)
dp[i][1] = The maximum when stepping on the i-th consecutively (came from i-1)

dp[i][0] = max(dp[i-2][0], dp[i-2][1]) + score[i]
dp[i][1] = dp[i-1][0] + score[i]

Time/Space Complexity

  • Time: O(n), Space: O(n) → can be optimized to O(1)

Problem 2: Baekjoon 1463 — Make It 1 (Silver III)

Link: https://www.acmicpc.net/problem/1463

Problem Summary

Find the minimum number of operations to make an integer n into 1 using the following three operations:

  1. If n is divisible by 3, divide by 3
  2. If n is divisible by 2, divide by 2
  3. Subtract 1

Recurrence Relation

dp[i] = the minimum number of operations to make i into 1
dp[1] = 0
dp[i] = dp[i-1] + 1
dp[i] = min(dp[i], dp[i//2] + 1)  if i % 2 == 0
dp[i] = min(dp[i], dp[i//3] + 1)  if i % 3 == 0

Time/Space Complexity

  • Time: O(n), Space: O(n)

Problem 3: Baekjoon 11726 — 2×n Tiling (Silver III)

Link: https://www.acmicpc.net/problem/11726

Problem Summary

Find the number of ways to fill a 2×n rectangle using 1×2 and 2×1 tiles, modulo 10007.

Recurrence Relation

dp[i] = Number of ways to fill a 2×i rectangle
dp[1] = 1  (1 vertical)
dp[2] = 2  (2 vertical or 2 horizontal)
dp[i] = dp[i-1] + dp[i-2]  (mod 10007)

Time/Space Complexity

  • Time: O(n), Space: O(n)

Solution File

  • boj_2579.py
  • boj_1463.py
  • boj_11726.py
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

boj_11726.pyPython
#Baekjoon 11726 — 2×n Tiling
# https://www.acmicpc.net/problem/11726
# Time: O(n)  Space: O(1)

import sys

input = sys.stdin.readline
MOD = 10_007


def solve() -> None:
    n = int(input())

    if n == 1:
        print(1)
        return

    #dp[i] = Number of ways to tile 2×i
    # dp[i] = dp[i-1] + dp[i-2]
    #Space optimization: Use only 2 variables
    a, b = 1, 2
    for _ in range(3, n + 1):
        a, b = b, (a + b) % MOD

    print(b)


solve()
boj_1463.pyPython
#Baekjoon 1463 — Make to 1
# https://www.acmicpc.net/problem/1463
# Time: O(n)  Space: O(n)

import sys

input = sys.stdin.readline


def solve() -> None:
    n = int(input())

    dp = [0] * (n + 1)
    #dp[i] = Minimum number of operations to make i into 1

    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + 1          #Operation 3: Subtract 1
        if i % 2 == 0:
            dp[i] = min(dp[i], dp[i // 2] + 1)
        if i % 3 == 0:
            dp[i] = min(dp[i], dp[i // 3] + 1)

    print(dp[n])


solve()
boj_2579.pyPython
#Baekjoon 2579 — Climbing Stairs
# https://www.acmicpc.net/problem/2579
# Time: O(n)  Space: O(n)

import sys

input = sys.stdin.readline


def solve() -> None:
    n = int(input())
    score = [0] + [int(input()) for _ in range(n)]

    if n == 1:
        print(score[1])
        return
    if n == 2:
        print(score[1] + score[2])
        return

    #dp[i][0]: Maximum score when stepping on the i-th stair alone (jumping from i-2)
    #dp[i][1]: Maximum score when stepping on the i-th stair consecutively (coming from i-1)
    dp = [[0, 0] for _ in range(n + 1)]

    dp[1][0] = score[1]
    dp[1][1] = 0          #There is no case of stepping on the 1st stair consecutively (start)
    dp[2][0] = score[2]   #Skip the 1st, and step on the 2nd
    dp[2][1] = score[1] + score[2]   #Step on 1st → 2nd consecutively

    for i in range(3, n + 1):
        dp[i][0] = max(dp[i - 2][0], dp[i - 2][1]) + score[i]
        dp[i][1] = dp[i - 1][0] + score[i]

    print(max(dp[n][0], dp[n][1]))


solve()

Learn this lesson with a mentor

Work through the Python Algorithms & Data Structures track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now