Memahami dua pilar dynamic programming: overlapping subproblems dan optimal substructure. Memoization (top-down) vs tabulation (bottom-up), pola 1D state, dan implementasi coin change dengan analisis jumlah state.

Setelah di episode 13 kita membahas Greedy Algorithms dan keterbatasannya, pada episode ini kita masuk ke Dynamic Programming (DP) — paradigma yang paling powerful untuk masalah dengan overlapping subproblems. DP pada dasarnya adalah divide & conquer yang dioptimasi: alih-alih menghitung ulang sub-masalah yang sama berkali-kali, kita menyimpan hasilnya dan menggunakan kembali.
DP adalah paradigma yang paling sering muncul di coding interview karena ia menguji kemampuan berpikir sistematis: mendefinisikan state, menulis transition equation, dan memilih urutan evaluasi. Pada episode ini kita memahami fondasi; episode 15-17 akan membahas pola DP yang lebih spesifik.
Sub-masalah yang sama muncul berulang kali dalam rekursi. Tanpa DP, setiap kemunculan dihitung ulang → waktu eksponensial. Dengan DP, setiap sub-masalah hanya dihitung sekali.
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2)
fib(3) = fib(2) + fib(1) ← sub-masalah yang sama muncul berulangSolusi optimal masalah besar mengandung solusi optimal dari sub-masalah. Ini memungkinkan kita membangun solusi dari bawah ke atas (tabulation) atau atas ke bawah (memoization).
Rekursif dengan cache: hitung secara rekursif, simpan hasil di memo, return dari memo jika sudah dihitung.
def fib_memo(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo)
return memo[n]Iteratif: bangun tabel dari sub-masalah terkecil ke terbesar, tanpa rekursi.
def fib_tab(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]| Aspek | Memoization | Tabulation |
|---|---|---|
| Pendekatan | Top-down (rekursif) | Bottom-up (iteratif) |
| Urutan evaluasi | Sesuai kebutuhan | Terurut dari awal |
| Stack overflow | Mungkin (rekursi dalam) | Tidak |
| Memori | Hanya state yang dihitung | Semua state |
| Debugging | Lebih intuitif | Perlu pemahaman urutan |
Tip
Mulai dari memoization jika kalian ingin memahami struktur masalah — ia lebih intuitif. Setelah transisi equation jelas, convert ke tabulation untuk performa (hindari overhead rekursi dan potensi stack overflow).
Masalah DP 1D: state hanya bergantung pada satu indeks — dp[i] merepresentasikan solusi optimal untuk prefix/suffix hingga posisi i.
Diberikan denominasi koin dan target amount, cari jumlah minimum koin yang dibutuhkan.
def coin_change(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0 # 0 koin untuk amount 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 dp[amount] if dp[amount] != float('inf') else -1
# coins = [1, 3, 4], amount = 6
# dp[0]=0, dp[1]=1, dp[2]=2, dp[3]=1, dp[4]=1, dp[5]=2, dp[6]=2
# 6 = 3 + 3 → 2 koin (bukan 4+1+1=3 koin seperti greedy)Transition equation: dp[i] = min(dp[i - coin] + 1) untuk semua coin ≤ i.
Mengapa greedy gagal (episode 13) tetapi DP berhasil: DP mengevaluasi semua kombinasi koin secara sistematis, sedangkan greedy hanya mempertimbangkan koin terbesar. Dengan denominasi [1, 3, 4], greedy memilih 4 dulu (dp[6] = dp[2] + 1 = 3 koin), tetapi DP menemukan 3+3 (dp[6] = dp[3] + 1 = 2 koin).
Jumlah state = jumlah sub-masalah unik yang harus diselesaikan. Untuk coin change:
Memori: O(amount) untuk tabel dp.
Note
Menghitung jumlah state adalah langkah pertama sebelum menulis kode DP. Jika jumlah state terlalu besar (misal 10^9), DP tidak feasible — cari pendekatan lain (greedy, math, atau approximation).
| Masalah | State | Transition |
|---|---|---|
| Fibonacci | dp[i] = i-th fib | dp[i] = dp[i-1] + dp[i-2] |
| Climb stairs | dp[i] = cara ke i | dp[i] = dp[i-1] + dp[i-2] |
| Coin change | dp[i] = min koin untuk i | dp[i] = min(dp[i-coin]+1) |
| House robber | dp[i] = max profit hingga i | dp[i] = max(dp[i-1], dp[i-2]+arr[i]) |
Pada episode 14 ini, kalian telah memahami:
dp[i] merepresentasikan solusi prefix hingga posisi i.dp[i] = min(dp[i-coin]+1) mengalahkan greedy.Di episode 15 selanjutnya kita akan membahas DP Klasik: Knapsack, LCS, dan Edit Distance — tiga masalah fundamental yang sering muncul di interview dan production. Sampai jumpa di episode 15!