Skip to content

Dynamic Programming | Computer Science

Dynamic programming (DP) solves problems by:

  1. Overlapping subproblems: The same subproblems are solved repeatedly.
  2. Optimal substructure: The optimal solution contains optimal solutions to subproblems.

Approaches:

  • Top-down (memoisation): Recursive with caching.
  • Bottom-up (tabulation): Fill a table iteratively from small subproblems to large.
AspectMemoisation (Top-Down)Tabulation (Bottom-Up)
ApproachRecursive with cacheIterative table fill
OrderNatural recursion orderDependency order
SpaceO(n)O(n) stack + O(n)O(n) tableO(n)O(n) table only
OverheadFunction call overheadMinimal
SubproblemsComputes only neededComputes all
Best forWhen not all subproblems neededWhen all needed

When to use which:

  • Use memoisation when the subproblem space is sparse (not all subproblems are needed).
  • Use tabulation when most subproblems are needed (avoids recursion overhead and stack overflow).
  • Both achieve the same asymptotic time complexity.

To prove that a problem has optimal substructure:

  1. Show that an optimal solution to the problem includes an optimal solution to a subproblem.
  2. Proved by contradiction: if the optimal solution contained a suboptimal sub-solution, replacing it with an optimal one would improve the overall solution.

Example (Shortest Path). If pp is a shortest path from uu to vv and ww is an intermediate vertex on pp Then the subpath of pp from uu to ww is a shortest path from uu to ww.

Proof. If not, there exists a shorter path p"p" from uu to ww. Then pp' concatenated with the subpath of pp from ww to vv would be shorter than ppContradicting that pp is a shortest path. \blacksquare

flowchart TD
A[5_Dynamic Programming] --> B[Key Concepts]
A --> C[Core Principles]
A --> D[Practical Applications]
B --> E[Fundamental definitions]
C --> F[Design patterns]
D --> G[Real-world usage]

Dynamic programming is solving complex problems by breaking them into overlapping subproblems. Think of it as a filing cabinet: instead of recalculating the same answer repeatedly, you store it and look it up later. The key insight is optimal substructure: the optimal solution to a problem contains optimal solutions to its subproblems. Memoisation (top-down) caches results of recursive calls, while tabulation (bottom-up) fills a table iteratively. DP is powerful but requires recognising the right subproblem decomposition.

Assuming optimal substructure without verification. Not all problems exhibit optimal substructure. The longest simple path problem, for example, does not: the longest simple path from uu to vv may not contain the longest simple path from uu to an intermediate vertex ww, because subpaths might share vertices with the rest of the path. Always prove optimal substructure before applying DP.

Incorrect state transitions. In the 0/1 knapsack, the recurrence is max(dp[i1][c],dp[i1][cwi]+vi)\max(dp[i-1][c], dp[i-1][c-w_i] + v_i), not max(dp[i][c],dp[i1][cwi]+vi)\max(dp[i][c], dp[i-1][c-w_i] + v_i). Using the current row instead of the previous row causes items to be counted multiple times. Trace through small examples to verify your recurrence.

Confusing memoisation and tabulation space complexity. Memoisation uses O(n)O(n) stack space plus O(n)O(n) table space, while tabulation uses only O(n)O(n) table space (or O(1)O(1) with rolling arrays). For deep recursion, memoisation may cause stack overflow. Choose tabulation when space is constrained or the recursion depth is large.