Exercises — Memoization (top-down DP) — recursive + memo dict
3.7.7 · D4· Coding › Algorithm Paradigms › Memoization (top-down DP) — recursive + memo dict
Shuru karne se pehle quick vocabulary reminder — har symbol ki kamayi:
Level 1 — Recognition
L1·Q1 — Kya memoization help bhi karta hai?
Har ek ke liye batao ki memoization speedup deta hai ya nahi, aur kyun.
(a) Fibonacci fib(n) = fib(n-1) + fib(n-2)
(b) Merge sort ek array ko half mein split karta hai, har half sort karta hai
(c) gcd(a, b) = gcd(b, a mod b) (Euclid)
Recall Solution
Memoization tabhi help karta hai jab subproblems overlap karein (same state ek se zyada baar maanga jaaye) aur problem mein optimal substructure ho (uska answer chhote subproblem answers se bana ho — vocabulary box dekho).
- (a) Fibonacci — YES.
fib(3)kofib(4)aurfib(5)dono request karte hain. Bahut bada overlap hai. Speedup: . - (b) Merge sort — NO. Har split array ka ek distinct slice produce karta hai; aap kabhi same slice ko dobara sort nahi karte. Yeh Divide and Conquer hai, DP nahi. Caching aisi cheezein store karti hai jo aap kabhi dobara look up nahi karte.
- (c) Euclid gcd — NO (essentially). Har call numbers ko strictly ek chain ke along shrink karta hai — koi repeated
(a,b)pair nahi aata. Yeh pehle se hi recursion hai bina kisi overlap ke.
Answer: sirf (a) ko faayda hota hai.
L1·Q2 — Chaar parts naam karo
Neeche diya code ek memoized function hai. Lines A, B, C, D ko chaar template parts mein se kissi ek se label karo.
def solve(n, memo):
if n in memo: return memo[n] # A
if n <= 1: return n # B
ans = solve(n-1, memo) + solve(n-2, memo) # C
memo[n] = ans # D
return memo[n]Recall Solution
- A = Check cache ("kya maine yeh state dekhi hai? stored answer return karo" — ek cache hit).
- B = Base case (sabse chhote inputs jinke answers known hain, koi recursion nahi).
- C = Recurse (chhote subproblems ke answers combine karo).
- D = Store (computed answer ko cache mein return se pehle save karo).
Parent se mnemonic: C-B-R-S — Check, Base, Recurse, Store. Dhyaan do ki har letter ab ek hi line se map karta hai: R (compute) aur S (save) deliberately alag hain taaki aap store karna return se pehle kabhi na bhoolo.
Level 2 — Application
L2·Q1 — Fibonacci Trace karo
L1·Q2 ka fib code use karte hue, fib(6) complete hone ke baad memo mein store hone wali exact values list karo, aur fib(6) do.
Recall Solution
Compute hone wale distinct states hain (values base case hit karti hain aur kabhi store nahi hoti). Fibonacci sequence: .
fib(6) = 8.
L2·Q2 — Climbing Stairs value
Parent ke ways code (, ) use karte hue, 7 stairs chadhne ke tarike count karo jab 1 ya 2 steps le sakte ho.
Recall Solution
Build up karo: .
ways(7) = 21.
(Dhyaan do yeh Fibonacci sequence shifted hai — kyunki yeh wahi recurrence hai alag base values ke saath.)
L2·Q3 — Grid Unique Paths value
Parent ke paths(r, c) code use karte hue, ek grid of cells mein unique paths count karo, yaani paths(2, 2) (0-indexed bottom-right corner).
Recall Solution
Figure walkthrough (s01): peach background pe rounded cells ka ek grid hai. Poori top row aur poora left column orange shaded hain aur unme se har ek mein number 1 likha hai — kyunki ek edge ke along sirf ek hi straight-line path hai (saare-right, ya saare-down). Chaar interior cells violet shaded hain; har interior number uske seedha upar wali orange/violet cell aur seedha baayein wali cell ka sum hai. Do magenta arrows bottom-right cell mein point karte hain: ek upar wali cell se neeche aata hai (value ) aur ek baayein se aata hai (value ), aur label likhta hai P(2,2)=6. Interior fill-in padhne par: , phir aur , aur finally corner.

paths(2,2) = 6.
Level 3 — Analysis
L3·Q1 — Distinct states count karo (2-D)
Ek grid ke liye, paths(r, c) tuple (r, c) se cache karta hai. Kitne distinct states exist karte hain, aur time aur space complexity kya hai?
Recall Solution
r range karta hai aur c range karta hai, isliye distinct states . Har state work karta hai (do lookups + ek add). Isliye:
Sabse deep recursion path top-left→bottom-right diagonal hai, length .
L3·Q2 — Exactly kyun, nahi?
Memoized Fibonacci mein, call tree ab bhi har node pe do branches mein split hota hai. Precisely explain karo ki runtime kyun hai, nahi.
Recall Solution
Distinct states count karo, calls nahi. Vocabulary se yaad karo: ek cache miss real work karta hai aur recurse karta hai; ek cache hit sirf mein stored value return karta hai bina branching ke. Kisi state ki pehli baar miss hota hai (real work); har baad ki baar hit hota hai.
Figure walkthrough (s02): fib(5) ke liye poora naive call tree draw kiya gaya hai — 15 circular nodes, har ek apne argument se labelled, edges ek node ko uske do children se jodti hain. Chhe magenta filled nodes wo hain jab har distinct argument () pehli baar reach hota hai — yeh woh cache misses hain jo real work karte hain. Saare faint violet nodes arguments ki repeat occurrences hain jo pehle se compute ho chuke hain — yeh cache hits hain aur, crucially, inhe neeche koi children nahi draw kiye gaye, kyunki hit instantly return karta hai aur kabhi expand nahi hota. Legend spell out karta hai "real work (6 distinct states)" vs "cache hit (pruned, )". Violet nodes ko childless leaves dekhna hi poori baat hai: exponential tree prune hokar 6 working nodes tak aa jaata hai.

L3·Q3 — Recursion tree size predict karo
Cache ke bina, naive fib(5) kitne total calls karta hai (call tree ke har node ko count karo, leaves bhi)? Memoization ke saath, kitne calls real (cache-miss) work karte hain?
Recall Solution
Naive: maano = fib(n) compute karne ke liye ki gayi total calls ki sankhya. fib(n) compute karne ke liye hum ek call karte hain (current invocation khud) plus uske do children ki saari calls. Woh "" current node hai — tree ka har node ek call count hota hai, aur yeh line hai jahan hum woh node count karte hain jis par hum khade hain:
( ke liye sirf ek single base-case call hai, isliye .) Evaluate karte hain: . 15 total calls.
Memoized: cache-miss states hain → 6 states real work karte hain (baaki cache hits hain). Yeh collapse concrete numbers mein hai.
Level 4 — Synthesis
L4·Q1 — Design: minimum coin count
Coins [1, 3, 4] aur ek target T diye hue hain, T tak sum karne ke liye sabse kam coins dhundho (unlimited coins, har ek). Recurrence aur memoized function likho, phir T = 6 ka answer compute karo.
Recall Solution
State: remaining amount t. Recurrence: t banane ke liye, har coin c ≤ t try karo, ek use karo, phir t-c optimally banao:
def min_coins(t, coins, memo=None):
if memo is None: memo = {}
if t in memo: return memo[t]
if t == 0: return 0 # base: amount 0 ke liye zero coins
best = float('inf')
for c in coins:
if c <= t:
best = min(best, 1 + min_coins(t - c, coins, memo))
memo[t] = best
return memo[t]T=6 ke liye compute karo: , , , , , (=4+1), (=3+3).
min_coins(6) = 2.
L4·Q2 — Design: longest common subsequence length
Do strings diye hue hain, unke longest common subsequence (LCS) ki length ke liye memoized recurrence define karo, state (i, j) ke saath = "A ka suffix i se aur B ka suffix j se consider karo". "ABCBDAB" aur "BDCAB" ka LCS length compute karo.
Recall Solution
State = start indices ki pair (i, j) — dono key mein hone chahiye, warna aap stale answers fetch karoge.
def lcs(i, j, A, B, memo=None):
if memo is None: memo = {}
if (i, j) in memo: return memo[(i, j)]
if i == len(A) or j == len(B): return 0
if A[i] == B[j]:
ans = 1 + lcs(i+1, j+1, A, B, memo)
else:
ans = max(lcs(i+1, j, A, B, memo), lcs(i, j+1, A, B, memo))
memo[(i, j)] = ans
return memo[(i, j)]"ABCBDAB" aur "BDCAB" ka ek longest common subsequence hai "BCAB" (length 4).
lcs = 4.
Level 5 — Mastery
L5·Q1 — Mutable-default bug
Yeh code pehle test pe pass hota hai lekin doosre pe fail karta hai. Exactly explain karo kya hota hai, aur corrected pehli line do.
def fib(n, memo={}):
if n in memo: return memo[n]
if n < 2: return n
memo[n] = fib(n-1) + fib(n-2)
return memo[n]
print(fib(5)) # test 1
print(fib(5)) # test 2 — same value, lekin cache leak ho gayaRecall Solution
Python memo={} ko ek baar evaluate karta hai, jab function define hota hai — har call pe nahi. Isliye wahi same dictionary object har top-level fib(...) invocation mein share hoti hai. Dono prints sahi 5 dete hain, lekin cache silently independent calls ke beech persist karta hai. Kisi competitive judge ya aisi test par jo state mutate kare (ya koi variant jahan recurrence external state pe depend kare), yeh stale cache baad ke results ko poison kar deta hai.
Fix — koi bhi:
def fib(n, memo=None):
if memo is None: memo = {}
...ya caller se ek explicit fresh dict pass karo, ya lru_cache decorator se @lru_cache use karo.
fib(5) = 5 (dono baar sahi value; danger state leakage hai, yeh specific number nahi).
L5·Q2 — Missing-state-variable bug
Ek knapsack solver sirf index se cache karta hai:
def knap(i, cap, items, memo=None):
if memo is None: memo = {}
if i in memo: return memo[i] # BUG
if i == len(items) or cap == 0: return 0
w, v = items[i]
skip = knap(i+1, cap, items, memo)
take = 0
if w <= cap:
take = v + knap(i+1, cap - w, items, memo)
memo[i] = max(skip, take)
return memo[i]Yeh galat kyun hai, aur correct cache key kya hai? Fixed lines do.
Recall Solution
Index i par answer dono i aur remaining capacity cap par depend karta hai. Do alag capacities same i reach karke alag answers produce karti hain, lekin sirf i se cache karne par woh collide kar jaati hain — pehle stored wali baad ki saari cap values ke liye return ho jaati hai. Yeh fast run karta hai aur sahi lagta hai, yahi exactly isse dangerous banata hai.
Rule: memo key mein har woh variable hona chahiye jo answer badalta ho.
Fix:
if (i, cap) in memo: return memo[(i, cap)]
...
memo[(i, cap)] = max(skip, take)
return memo[(i, cap)]L5·Q3 — Top-down ko bottom-up mein convert karo
Climbing Stairs recurrence , lo. Bottom-up version likho aur confirm karo ki woh wahi ways(7)=21 deta hai.
Recall Solution
Bottom-up ek table ko base cases se upward fill karta hai — koi recursion nahi, isliye koi stack-overflow risk nahi:
def ways(n):
if n <= 1: return 1
dp = [0] * (n + 1)
dp[0] = dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]n=7 ke liye table: [1,1,2,3,5,8,13,21] → ways(7) = 21, memoized answer se identical. Same recurrence, same complexity , alag control flow.
Recall Master checklist (saare levels khatam karne ke baad reveal karo)
Poore page ka ek-line summary ::: Honest recurrence likho, cache ko poore state se key karo, return se pehle store karo, aur fresh dict pass karo — phir distinct states count karke cost karo.
Connections
- Memoization (top-down DP) — recursive + memo dict (index 3.7.7) — parent note
- Recursion — yahan har solution recursion + ek cache hai
- Dynamic Programming — woh paradigm jo yeh exercises train karti hain
- Tabulation (Bottom-up DP) — L5·Q3 isme convert karta hai
- Time Complexity Analysis — L3 counting-states technique
- Divide and Conquer — L1·Q1 (b),(c) yahi hain, DP nahi
- Hash Maps / Dictionaries — cache
- lru_cache decorator — L5·Q1 one-line fix