Practice set — covers SL No 70–71 (memoization, tabulation & the two DP conditions). Answer each from memory before opening the solution.
-
💡 Solution & explanation
Answer: Optimal substructure (the optimal solution is built from optimal solutions of sub-problems) and overlapping sub-problems (the same sub-problems recur many times).
Explanation: If sub-problems don't overlap, plain divide & conquer suffices; DP's payoff comes from reusing repeated sub-problem results.
-
💡 Solution & explanation
Answer: Memoization is top-down: recurse naturally and cache each result (e.g. in a dict) so it's computed once. Tabulation is bottom-up: iteratively fill a table from the smallest sub-problems up to the answer.
Explanation: Memoization is easy to add to a recursive solution; tabulation avoids recursion overhead and can be more space-efficient.
-
💡 Solution & explanation
Answer: By caching each
fib(n)the first time it's computed, every value is calculated once, turningO(2ⁿ)intoO(n)time.from functools import lru_cache @lru_cache(maxsize=None) def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2)
Explanation: The naive version recomputes the same
fibvalues exponentially often (overlapping sub-problems). Caching removes that redundant work;@lru_cacheis built-in memoization.
-
💡 Solution & explanation
Answer: Memoization uses the cache plus the recursion call stack (risking deep-recursion limits); tabulation uses an explicit table but no recursion stack, and can often be optimized to keep only the last few rows/values.
Explanation: For example, bottom-up Fibonacci can run in
O(1)space by tracking just the previous two numbers — a common DP space optimization.