Skip to main content

Welcome to a Grasp lesson

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

Lesson 44

Unlocking Dynamic Programming: Optimal Substructure & Overlapping Subproblems

Recommended resources

Hello! Welcome to the next lesson in our module on "Algorithms: Sorting, Searching, and Design."

Introduction

In our last lesson, we delved into powerful "divide and conquer" algorithms like Merge Sort and Quick Sort. You learned how they break a large problem into smaller, independent subproblems, solve them recursively, and then combine the results. This approach gave us a significant performance boost, moving from to .

Today, we will explore another, even more powerful algorithmic paradigm called Dynamic Programming (DP). Like "divide and conquer," DP also involves breaking down problems. However, it is specifically designed for problems where the subproblems are not independent—they overlap.

Your learning outcome for this lesson is to: Describe the principles of dynamic programming (optimal substructure, overlapping subproblems).

By the end of this lesson, you will be able to identify the two key characteristics a problem must have to be solvable with Dynamic Programming. This conceptual foundation is essential for your GATE preparation and will be the basis for our next lesson, where we'll start implementing DP solutions.


What is Dynamic Programming?

At its core, Dynamic Programming is an optimization technique. It's about solving a complex problem by breaking it down into a collection of simpler subproblems, solving each of those subproblems just once, and storing their solutions. The next time the same subproblem occurs, instead of recomputing its solution, you simply look up the previously computed solution.

A great way to think about it is "remembering the past to solve the future faster."

To get a simple, intuitive definition, let's watch a short clip.

Dynamic Programming For Beginners

The video 'Dynamic Programming For Beginners' from the Kantan Coding channel provides a very clear, layman's definition of DP.

Watch the section from 02:28 to 03:11. Focus on the core idea of breaking down a problem and reusing solutions.

The crucial difference from the Merge Sort we saw previously is this idea of "reusing solutions." In Merge Sort, the subproblems (sorting the left half and right half of an array) are distinct and don't overlap. In DP, the subproblems are intertwined, and we solve the same ones multiple times if we're not careful.


The Two Core Principles of Dynamic Programming

For a problem to be a candidate for a Dynamic Programming solution, it must exhibit two fundamental properties:

  1. Optimal Substructure
  2. Overlapping Subproblems

A problem must have both of these characteristics. If it only has an optimal substructure, a simpler recursive or greedy algorithm might be sufficient. If it has overlapping subproblems without an optimal substructure, DP won't lead to a correct solution.

Dynamic Programming Problem Principles Venn Diagram
This Venn diagram shows that Dynamic Programming applies to problems that have both Optimal Substructure and Overlapping Subproblems.

To get a formal definition of these two concepts, take a quick look at the following resource.

Overview of Dynamic Programming

The article 'Overview of Dynamic Programming' from Codefinity provides concise definitions of our two key concepts.

Read the two bullet points under the heading 'Key Concepts in Dynamic Programming'. This will give you a clear, textual definition of both principles.

Now, let's explore each of these principles in more detail with examples.

1. Optimal Substructure

A problem has an optimal substructure if the optimal solution to the whole problem can be constructed from the optimal solutions of its subproblems.

This might sound familiar because this is the same property we saw in "divide and conquer" algorithms. Let's look at an intuitive example.

A Beginner's Guide to Dynamic Programming

The video 'A Beginner's Guide to Dynamic Programming' by Matt Guest explains optimal substructure using a very intuitive shortest-path problem.

Watch from 01:28 to 02:30. Notice how the overall shortest path is naturally composed of the shortest paths of its segments.

As the video showed, if the shortest path from San Francisco to San Diego goes through Los Angeles, then the path used from SF to LA must be the shortest path between those two cities. You can't have an overall optimal solution that uses a suboptimal solution for one of its parts.

The Fibonacci sequence is another classic example. The solution to Fib(n) is Fib(n-1) + Fib(n-2). The solution to the main problem is constructed directly from the solutions to its subproblems.

2. Overlapping Subproblems

This is the property that truly distinguishes Dynamic Programming problems. It means that a naive recursive solution would solve the same subproblems repeatedly.

Let's use the Fibonacci sequence again. To calculate Fib(5), a simple recursive algorithm would work like this:

  • Fib(5) calls Fib(4) and Fib(3)
  • Fib(4) calls Fib(3) and Fib(2)
  • Fib(3) calls Fib(2) and Fib(1)

Notice that Fib(3) is being calculated twice, once as a direct subproblem of Fib(5) and again as a subproblem of Fib(4). Fib(2) will be calculated three times! This is a huge waste of computation. These repeated calculations are the "overlapping subproblems."

This recursion tree illustrates the problem perfectly.

Dynamic Programming: Fibonacci Recursion Tree
A recursion tree for calculating `Fib(4)`. Notice how `Fib(2)` is computed twice, and `Fib(1)` and `Fib(0)` are computed multiple times. These are the overlapping subproblems.

To see this explained visually, let's return to the Kantan Coding video.

Dynamic Programming For Beginners

This clip visually demonstrates the overlapping subproblems in the Fibonacci recursion tree.

Watch from 05:41 to 07:00. The presenter highlights the exact subproblems that are being solved more than once.

This redundancy is exactly what Dynamic Programming aims to eliminate by storing the result of each subproblem the first time it's calculated.

Test your understanding!

Consider calculating Fib(6) recursively. How many times would the subproblem Fib(2) be calculated?

Hint: You can draw a recursion tree similar to the one above.
Fib(6) -> Fib(5) + Fib(4)
Fib(5) -> Fib(4) + Fib(3)
Fib(4) -> Fib(3) + Fib(2)

Show answer

Let's trace the calls:

  1. Fib(6) calls Fib(5) and Fib(4).
  2. The call to Fib(5) will eventually lead to a call for Fib(2).
  3. The call to Fib(4) will also lead to a call for Fib(2).
  4. Let's break it down further:
    • Fib(6) calls Fib(5) which calls Fib(4) which calls Fib(3) which calls Fib(2). (1st time)
    • Fib(6) calls Fib(5) which calls Fib(4) which calls Fib(2). (2nd time)
    • Fib(6) calls Fib(5) which calls Fib(3) which calls Fib(2). (3rd time)
    • Fib(6) calls Fib(4) which calls Fib(3) which calls Fib(2). (4th time)
    • Fib(6) calls Fib(4) which calls Fib(2). (5th time)

So, Fib(2) would be calculated 5 times. This shows how quickly the number of redundant calculations grows!


Why These Principles Lead to Optimization

The presence of overlapping subproblems in a recursive solution almost always leads to an exponential time complexity, , because the number of function calls grows exponentially with the input size n.

By identifying that a problem has both optimal substructure and overlapping subproblems, we know we can apply DP. By storing the results of subproblems (a process we will later call memoization or tabulation), we ensure each subproblem is only computed once. This typically reduces the time complexity from exponential to polynomial (e.g., or ), which is a massive improvement.

Dynamic Programming For Beginners

This final clip explains why the overlapping subproblems make the naive recursive solution so inefficient.

Watch from 07:45 to 08:35 to understand how the repeated work leads to exponential time complexity and why DP is necessary.


Conclusion

In this lesson, we established the conceptual groundwork for Dynamic Programming, a critical topic for your GATE exam and for computer science in general.

Key Takeaways:

  • Dynamic Programming is an optimization technique for problems that can be broken down into smaller, repeating subproblems.
  • To be solvable with DP, a problem must have two key properties:
    1. Optimal Substructure: The optimal solution to the overall problem is composed of the optimal solutions to its subproblems.
    2. Overlapping Subproblems: A naive recursive solution would compute the same subproblems multiple times, leading to inefficiency.
  • DP works by storing the solutions to subproblems to avoid re-computation, drastically improving performance from exponential to polynomial time complexity.

Preview of the Next Lesson:

Now that you know how to identify a DP problem, the next logical step is to learn how to solve one. In the next lesson, we will focus on the two main implementation techniques for Dynamic Programming:

  1. Memoization (Top-Down Approach)
  2. Tabulation (Bottom-Up Approach)

We will apply both of these techniques to efficiently solve the Fibonacci sequence problem, turning our theoretical understanding into practical code.