Hello! In our previous lesson, we worked through the Edit Distance problem, using a 2D dynamic programming table to find the optimal sequence of edits between two strings.
Today, we'll tackle the final problem in our module on classic DP applications: the Coin Change problem. This is a foundational problem that appears in many variations and showcases a different DP pattern than the sequence alignment problems we've studied. Our goal is to find the minimum number of coins required to make a specific amount, given a set of coin denominations.
This problem has direct applications in various optimization scenarios, from inventory management to resource allocation. Your experience in software engineering might bring to mind scenarios where you need to find the most efficient way to achieve a target value using a given set of components.
1. Understanding the Coin Change Problem
The problem statement is as follows:
Given a set of coin denominations (e.g., {1, 5, 10, 25}) and a target amount
A, find the minimum number of coins that sum up toA. You can assume an unlimited supply of each coin denomination.
For example, to make change for $20 with coins {$5, $10, $20}, you could use four $5 coins, two $10 coins, or one $20 coin. The optimal solution is one coin.

If it's impossible to make the target amount, we typically return an indicator like -1 or infinity.
2. Formulating the DP Solution
Like other DP problems, Coin Change has the properties of optimal substructure and overlapping subproblems.
- Optimal Substructure: The minimum number of coins to make amount
Acan be found by first using one coin (say, of valuec) and then finding the minimum number of coins for the remaining amountA - c. The optimal solution forAis built upon the optimal solutions for smaller amounts. - Overlapping Subproblems: When calculating the solution for
A, you might find yourself recalculating the solution for a smaller amount (e.g.,A - 5 - 1andA - 1 - 5) multiple times.
This structure suggests that we can define our subproblem as:
Let dp[i] be the minimum number of coins required to make change for amount i.
Our goal is to compute dp[A].
Top-Down Intuition (Recursion)
A natural way to think about this is recursively. To find the minimum coins for an amount A, we can try using each available coin c as the last coin. If we use coin c, the total number of coins will be 1 + (minimum coins for amount A - c). We do this for all possible coins and take the minimum.
This recursive approach, however, leads to many redundant calculations. The video below does an excellent job of visualizing this recursion as a tree and highlighting the duplicate subproblems that make it a perfect candidate for dynamic programming.
The Change Making Problem - Fewest Coins To Make Change Dynamic Programming
To understand the recursive nature of the problem, watch this segment from the video "The Change Making Problem" by Back To Back SWE. It clearly illustrates how the main problem breaks down into smaller, overlapping subproblems.
Watch from 00:23 to 07:41. Pay close attention to how the decision tree is formed and how the same subproblems (like making change for amount '9' or '5') appear in different branches of the tree. This demonstrates the need for memoization or tabulation.
3. The Bottom-Up (Tabulation) Approach
While the top-down approach is intuitive, the bottom-up approach is often more efficient to implement iteratively. We'll build a 1D array, dp, of size A + 1, where dp[i] will store the minimum coins for amount i.
-
Initialization:
dp[0] = 0, because zero coins are needed to make an amount of 0.- Initialize all other
dp[i]to a large value (like infinity orA + 1) to signify that they are not yet reachable. An amountAcan never require more thanAcoins (if a 1-cent coin exists), soA+1serves as a good stand-in for infinity.
-
Iteration: We iterate from
i = 1up toA. For each amounti, we iterate through our available coin denominationsc. If a coincis less than or equal toi, we can potentially use it. The recurrence relation is:In words: the minimum coins for amount
iis the minimum of its current value and1 + (the minimum coins for amount i - c). We updatedp[i]for each coin we can use.
This process systematically builds the solution from amount 1 all the way to A, ensuring that when we calculate dp[i], the values for all smaller amounts (like dp[i-c]) have already been optimally computed.
The following video provides a step-by-step walkthrough of filling this DP table. Seeing it done visually is often the best way to solidify the concept.
The Change Making Problem - Fewest Coins To Make Change Dynamic Programming
Let's watch the bottom-up table being constructed. This segment from the same Back To Back SWE video meticulously fills each cell of the DP array, explaining the logic at every step.
Watch from 07:41 to 20:05. This is a detailed walkthrough. Try to predict the value of the next cell before the presenter reveals it. This will help you internalize the recurrence relation.
A More Formal View
For a more formal, mathematical perspective, we can reference the tutorial solutions from the University of Toronto. While they initially define a 2D array, they also describe the more common 1D array approach we've just discussed.
CSC373 Summer '22 Tutorial 3 Solutions
This document provides a formal 'Bellman equation' for the 1D array approach. This will connect our practical implementation to the underlying theory, which you appreciate.
Read the note at the end of section (d) on page 2. It starts with "Note: You can also solve this question using a 1D array..." and presents the recurrence relation for D[i], which is equivalent to our dp[i].
The recurrence D[i] = 1 + min_{j:d[j]≤i} D[i −d[j]] is precisely the logic we apply in our nested loops: for a given amount i, we find the minimum over all possible coin choices j.
4. Complexity Analysis
Your preference for understanding the "why" includes analyzing performance. Let's break down the complexity of our bottom-up solution.
-
Time Complexity:
- We have an outer loop that runs from
1toA(the target amount). - Inside it, we have an inner loop that iterates through all
Ccoin denominations. - This gives us a total time complexity proportional to the amount multiplied by the number of coins.
- We have an outer loop that runs from
-
Space Complexity:
- We need an array of size
A + 1to store the intermediate solutions for each amount from 0 toA.
- We need an array of size
The presenter in the video LINK confirms this analysis in the final part of the video (20:05 - 22:12).
5. A Note on Variations
The "Coin Change" problem is a family of problems. The version we solved is the most common one, sometimes called Unbounded Knapsack because you have an unlimited supply of each item (coin).
It's useful to be aware of two other major variations:
-
Total Unique Ways to Make Change (Coin Change 2): Here, the goal is not to find the minimum number of coins, but to count the number of distinct combinations of coins that sum to the target amount. This requires a different DP state and recurrence. The video "Total Unique Ways To Make Change" (LINK) covers this variant if you're curious.
-
Minimum Coins with Limited Supplies (Bounded Knapsack): This is the version where you have a finite count of each coin denomination (e.g., three 5-cent coins, two 10-cent coins). This adds another dimension to the problem, making it more similar to the 0/1 Knapsack problem we've already seen. The article "Tackling the 'Minimum Coins with Limited Supplies' Problem" (LINK) explores this more complex scenario.
Being able to distinguish between these variants is a key skill in applying the correct DP pattern.
Conclusion
In this lesson, we mastered the classic Minimum Coin Change problem using dynamic programming. This concludes our module on foundational DP applications.
Key Takeaways:
- The Minimum Coin Change problem seeks the fewest coins to form a target amount, assuming an unlimited supply of each denomination.
- The optimal subproblem is defined as
dp[i]: the minimum coins needed to make change for amounti. - The recurrence relation for the bottom-up approach is
dp[i] = min(dp[i], 1 + dp[i - c])for each coinc. - The solution has a time complexity of and a space complexity of .
Preview of the Next Lesson:
We are now ready to move into our next module, "Advanced Algorithmic Patterns". We'll begin by looking at algorithms on graphs, specifically Topological Sorting on a Directed Acyclic Graph (DAG). This is a fundamental operation with applications in scheduling tasks, resolving dependencies in software builds, and more.