Skip to main content

Welcome to a Grasp lesson

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

Lesson 29

DP Recurrence Relation Formulation

Recommended resources

Hello! Welcome to your next lesson in our journey through Dynamic Programming.

In our previous session, we established the two essential properties that make a problem a candidate for Dynamic Programming: optimal substructure and overlapping subproblems. You learned how to identify these problems, which is the crucial first step.

Today, we move from identifying to defining. Our goal is to translate our understanding of a problem's structure into a precise mathematical formula. This formula is called a recurrence relation. Mastering this step is arguably the most creative and critical part of solving any DP problem, as it serves as the blueprint for your final code.

What is a Recurrence Relation?

You've already encountered a recurrence relation with the Fibonacci sequence:

A recurrence relation in DP is an equation that defines the solution to a problem in terms of the solutions to its smaller, constituent subproblems. It always consists of two key parts:

  1. The Recursive Step: The formula that relates the larger problem to smaller ones (e.g., Fib(n-1) + Fib(n-2)).
  2. The Base Case(s): The smallest subproblems whose solutions are known directly, without needing further recursion (e.g., Fib(0) = 0, Fib(1) = 1). These prevent the recursion from going on forever.
How to Solve Dynamic Programming Problems: A Step-by-Step Guide
This image outlines a general process for tackling DP problems. We're focusing on steps 3 and 4: expressing the recurrence relation and identifying the base cases.

A Systematic Approach to Formulating a Recurrence

Developing a recurrence relation isn't magic; it's a structured thought process. We can break it down into three steps, which we'll apply to every DP problem.

Step 1: Define Your Subproblem (The State)

Before you can write a formula, you must precisely define what you're trying to solve. This means defining a function, say DP(i, j, ...), and stating exactly what it calculates. The parameters i, j, ... represent the state of the subproblem. Getting this definition right is more than half the battle.

CS/ECE 374 A (Spring 2024) Tips on Dynamic Programming

The document 'Tips on Dynamic Programming' from a university course offers excellent, rigorous advice on this crucial first step. It explains how to think about parameters and what the output of your subproblem function should represent.

Please read section 1, 'Definition of subproblems'. Pay close attention to how a subproblem is typically defined using parameters and how the final answer to the original problem relates to these subproblems.

Step 2: Find the Transition (The Recursive Formula)

Once you've defined your subproblem, say DP(n), you need to figure out how to compute its value using the solutions to smaller subproblems (like DP(n-1), DP(k) where k < n, etc.).

A powerful mental trick here is to think about the last step or final choice that leads to the solution for DP(n).

  • If the problem asks for a maximum or minimum value, you'll typically take the max or min over all possible last choices.
  • If the problem asks to count the number of ways, you'll typically sum the results of all possible last choices.

This "last step" logic is what forms the recursive part of your relation.

Step 3: Determine the Base Cases

Your recurrence breaks down a problem into smaller and smaller pieces. The base cases are the smallest pieces whose answers are known trivially, without any further calculation. They are the "end points" of your recursion. For example, the number of ways to climb 0 stairs, or the minimum coins to make change for $0.

Let's put this three-step process into action with some examples.


Example 1: Climbing Stairs (A Counting Problem)

Let's start with a classic introductory problem.

Problem: You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Let's apply our three-step process.

Mastering Dynamic Programming

The article 'Mastering Dynamic Programming' walks through this exact problem. It's a great example to see the process from start to finish.

Please read 'Example 1: Climbing Stairs'. The article will guide you through defining the base cases and deriving the recurrence relation.

Let's summarize the logic from the article using our framework:

  1. Define Subproblem: Let ways(i) be the number of distinct ways to climb to the i-th step. Our goal is to find ways(n).

  2. Find Transition: To reach step i, what could have been our last move?

    • We could have taken a single step from i-1. The number of ways to get to i-1 is ways(i-1).
    • We could have taken two steps from i-2. The number of ways to get to i-2 is ways(i-2).
      Since these are mutually exclusive options, the total number of ways to reach step i is the sum of the two.
      Recurrence: ways(i) = ways(i-1) + ways(i-2)
  3. Define Base Cases:

    • ways(1) = 1 (Only one way to get to the first step: a single 1-step).
    • ways(2) = 2 (Two ways to get to the second step: 1+1 or 2).

We have successfully formulated the recurrence relation for the climbing stairs problem!


Example 2: The Coin Change Problem (An Optimization Problem)

Now let's tackle a problem that involves finding a minimum value.

Problem: You are given an array of coin denominations coins and a total amount m. Find the minimum number of coins that you need to make up that amount. Assume an infinite supply of each coin.

This is a classic DP problem. Let's watch a video that derives the recurrence.

Mastering Dynamic Programming - How to solve any interview problem (Part 1)

In 'Mastering Dynamic Programming - How to solve any interview problem', the host provides a clear, step-by-step derivation for the coin change problem.

Watch the video from 07:47 to 11:32. The host starts by defining the subproblem and then uses the 'last choice' logic to build the recursive formula.

Let's recap the formulation from the video:

  1. Define Subproblem: Let min_coins(m) be the minimum number of coins required to make the amount m. We want to find min_coins(target_amount).

  2. Find Transition: To make change for amount m, what was the last coin we added? It could be any coin c from our set of coins. If the last coin we added was c, then before that, we must have had an amount of m-c, and we must have formed it with the minimum number of coins, which is min_coins(m-c).
    So, for each possible last coin c, the total number of coins would be 1 + min_coins(m-c).
    Since we want the overall minimum, we must try all possible last coins and choose the best option.
    Recurrence: min_coins(m) = 1 + min(min_coins(m - c)) for every coin c in coins where m >= c.

  3. Define Base Case:

    • min_coins(0) = 0. It takes 0 coins to make an amount of 0.
Test your understanding!

Using the recurrence we just defined for the Coin Change problem with coins = {1, 4, 5} and a target amount of 8, what is the formula for min_coins(8)?

Show answer

The formula would be:
min_coins(8) = 1 + min(min_coins(8-1), min_coins(8-4), min_coins(8-5))
min_coins(8) = 1 + min(min_coins(7), min_coins(4), min_coins(3))

To find the actual value, we would need to solve these subproblems recursively.


Generalizing the Recurrence Relation

The process of defining the subproblem and then considering the "last choice" to form the transition is a universal technique in DP.

5 Simple Steps for Solving Dynamic Programming Problems

Let's watch a segment from '5 Simple Steps for Solving Dynamic Programming Problems' by Reducible, which brilliantly visualizes how to find subproblems and generalize their relationships into a recurrence formula, using the Longest Increasing Subsequence (LIS) problem as an example.

Watch the video from 03:09 to 06:47. Observe how the presenter: Defines the subproblem as 'LIS ending at index k'. Finds the relationships by asking which previous subproblems are needed. Generalizes this into a formal recurrence relation.

The video demonstrates a key idea: a good subproblem definition makes the transition logic almost naturally fall into place. If you're struggling to find the recurrence, you may need to revisit and redefine your subproblem, perhaps by adding another parameter to the state.


Conclusion

In this lesson, you've learned the most fundamental skill in dynamic programming: how to formulate a recurrence relation. This is the intellectual core of any DP solution.

Key Takeaways:

  • A recurrence relation is the mathematical blueprint of a DP solution, consisting of a recursive step and base cases.
  • A systematic, three-step approach is highly effective for formulation:
    1. Define the Subproblem (State): Clearly state what DP(...) computes.
    2. Find the Transition (Recurrence): Use the "last choice" logic to relate the problem to its subproblems. This usually involves a min, max, or sum operation.
    3. Determine the Base Cases: Identify the smallest subproblems that can be solved directly.

Up Next:
You now know how to identify a DP problem and how to write the recurrence relation for it. The next logical step is to turn that relation into code. In our next lesson, we will focus on the bottom-up (tabulation) approach. You'll learn how to systematically solve all subproblems from smallest to largest, storing the results in a table (like an array or a 2D list), which is a highly efficient and common technique in competitive programming.