Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 14

Dynamic Programming Fundamentals: Optimal Substructure and Overlapping Subproblems

Recommended resources

Welcome to a new module in your journey through competitive programming algorithms. In the previous module, we mastered graph traversals like BFS and DFS, applying them to solve a variety of problems, from finding shortest paths to checking for bipartiteness. Now, we're taking a deliberate step aside from graphs to build a crucial, parallel skill: Dynamic Programming (DP). You'll soon see that a solid understanding of DP is essential for solving more complex graph problems, particularly those involving Directed Acyclic Graphs (DAGs), which we'll cover in the next module.

Today's lesson is the first step into this powerful paradigm. Our goal is to define optimal substructure and overlapping subproblems, the two hallmarks of dynamic programming. By the end of this lesson, you'll be able to look at a problem and identify the core properties that make it a candidate for a DP solution.

What is Dynamic Programming?

At its core, Dynamic Programming is an algorithmic technique for solving problems, typically optimization or counting problems, by breaking them into a collection of simpler subproblems. This might sound a lot like the "divide and conquer" strategy you've likely encountered before. However, DP has a unique twist: the subproblems are not independent.

This diagram illustrates the core idea of Dynamic Programming. It builds on the 'Divide and Conquer' paradigm but is specifically applied to problems that exhibit both 'Overlapping Subproblems' and 'Optimal Substructure'. This leads to two main implementation methodologies: 'Memoization' and 'Tabulation'.

The diagram above provides a high-level map of what we'll be exploring. A problem is suitable for DP if it has two specific properties. Let's examine each one in detail.


Hallmark 1: Optimal Substructure

A problem is said to have optimal substructure if an optimal solution to the overall problem can be constructed from the optimal solutions of its subproblems. This property is what allows us to formulate a recursive solution in the first place.

Let's make this concrete with an example you're already familiar with: the shortest path problem. Imagine finding the shortest path from a vertex to a vertex . If this path passes through an intermediate vertex , then the portion of the path from to must be a shortest path from to . Similarly, the portion from to must be a shortest path from to . Why? Because if there were a shorter path from to , we could substitute it into our original path to create a shorter path from to , which contradicts our starting assumption.

This "proof by contradiction" is the classic way to confirm optimal substructure. The following video explains this concept clearly using the shortest path example.

Dynamic Programming | Set 2 (Optimal Substructure Property) | GeeksforGeeks

This GeeksforGeeks video provides a concise definition of the Optimal Substructure property and illustrates it perfectly with the shortest path problem.

Watch the segments from the beginning to the shortest path example. This will formalize the intuition we just discussed.

For a more rigorous definition, which aligns with your preference for theoretical underpinnings, let's turn to a set of lecture notes from Carnegie Mellon University.

Dynamic Programming I

These notes provide an excellent, formal explanation of optimal substructure.

Please read the section that starts with the definition. Pay close attention to the proof by contradiction for the shortest path problem, as it is the fundamental way to reason about this property.

Not all problems have optimal substructure. The same video provides an excellent counterexample: finding the longest simple path (a path with no repeated vertices) in a general graph.

Dynamic Programming | Set 2 (Optimal Substructure Property) | GeeksforGeeks

This segment shows why the longest path problem generally lacks optimal substructure, which helps clarify what the property is by showing what it is not.

Watch from the counterexample. The key insight is that combining two optimal sub-paths might create a cycle, which is disallowed, or might not form the globally optimal main path.


Hallmark 2: Overlapping Subproblems

The second critical property is overlapping subproblems. This means that when we break the problem down recursively, we find ourselves solving the exact same subproblems multiple times. This is the key difference from standard divide and conquer algorithms like Merge Sort, where subproblems are always unique (e.g., sorting the left half of an array is completely disjoint from sorting the right half).

The classic example to demonstrate this is calculating the -th Fibonacci number, which is defined by the recurrence relation:

with base cases and .

A naive recursive function would directly translate this recurrence into code. Let's see what happens when we try to compute .

The recursion tree for calculating `Fib(5)`. Notice how `Fib(3)` is computed twice, `Fib(2)` is computed three times, and so on. This redundancy is the essence of overlapping subproblems.

As the image shows, the computation of requires and . To compute , we need and . We are already computing twice! This redundancy grows exponentially as increases, leading to a very inefficient algorithm.

The following video walks through this exact scenario and formally introduces the concept.

Dynamic Programming | Set 1 (Overlapping Subproblems Property) | GeeksforGeeks

This video introduces the Fibonacci sequence as a simple recursive problem and then uses its recursion tree to clearly define and illustrate the Overlapping Subproblems property.

Watch from the beginning to define the property. The visual breakdown of the function calls for fib(5) is particularly helpful for understanding the repeated computations.


The Dynamic Programming Recipe

When a problem exhibits both optimal substructure and overlapping subproblems, it is a perfect candidate for Dynamic Programming.

  1. Optimal Substructure gives us a recursive formula to express the solution of a large problem in terms of its subproblems.
  2. Overlapping Subproblems tells us that a naive recursive implementation will be inefficient. But, crucially, it also suggests an optimization: if we save the result of a subproblem the first time we compute it, we can simply look it up the next time we need it, avoiding re-computation.

This simple idea of storing and reusing solutions is the essence of DP. It turns an exponential-time recursive algorithm into a much more efficient polynomial-time one. This leads to two main implementation strategies:

  • Memoization (Top-Down): We write the simple recursive solution and add a mechanism (like a hash map or an array) to cache the results of subproblems. This is often the most intuitive approach.
  • Tabulation (Bottom-Up): We figure out the order in which subproblems need to be solved and fill up a table of solutions iteratively, starting with the smallest subproblems and building up to the final solution.

We will dive deep into these two techniques in the upcoming lessons. For now, let's read a concise summary that ties these two properties together.

[PDF] 1 More on the Bellman-Ford Algorithm - CS 161

These lecture notes from Stanford provide a crisp, formal "recipe" for dynamic programming, explicitly listing our two key properties.

Please read the section Dynamic Programming Algorithm Recipe. This serves as a perfect summary of what we've discussed.


Conclusion

In this lesson, we've laid the conceptual groundwork for Dynamic Programming. You now understand the two conditions that make a problem amenable to a DP solution.

Key Takeaways:

  • Dynamic Programming is an algorithmic paradigm that solves complex problems by breaking them into simpler, overlapping subproblems.
  • Optimal Substructure is the property where the optimal solution to a problem can be constructed from the optimal solutions of its subproblems. It allows for a recursive formulation.
  • Overlapping Subproblems is the property where a naive recursive solution would compute the same subproblems repeatedly. This redundancy is what DP aims to eliminate.
  • A problem must have both properties to be a good candidate for DP.

You are now equipped to identify the signature of a DP problem. In our next lesson, we will put this theory into practice. We will implement the naive, exponential-time recursive solution for Fibonacci numbers, and then apply memoization to optimize it to linear time, seeing firsthand how powerful this technique can be.