Introduction
The Coin Change Problem is a classic and very important Dynamic Programming (DP) problem in DSA. It is frequently asked in coding interviews because it tests your understanding of optimization, decision making, and DP table construction.
In simple words, this problem is about finding the minimum number of coins needed to make a given amount using the available coin denominations.
This article explains the Coin Change problem in very simple, human-friendly language, starting from the basics and progressing to interview-level understanding.
What is the Coin Change Problem?
You are given:
Your task is to find the minimum number of coins required to make that amount.
Important rules:
Example
Coins = [1, 2, 5]
Amount = 11
Output
3
Explanation
The minimum number of coins needed to make 11 is:
5 + 5 + 1 = 11
So the answer is 3 coins.
Why Greedy Approach Does Not Always Work
A greedy approach always picks the largest coin first.
This may work for some cases but fails for others.
Example Where Greedy Fails
Coins = [1, 3, 4]
Amount = 6
Greedy picks:
But optimal solution is:
So we need a better approach.
Why Dynamic Programming is Needed
Dynamic Programming helps us:
Break the problem into smaller subproblems
Store results to avoid recomputation
Find the optimal solution efficiently
This makes DP the best choice for the Coin Change problem.
Dynamic Programming Approach (Core Idea)
DP State Definition
We create a DP array where:
Base Case
All other values are initialized to a large number.
Step-by-Step DP Explanation
Steps:
Create a DP array of size amount + 1
Initialize all values with a large number
Set dp[0] = 0
For each amount from 1 to target amount:
If dp[amount] is still large, return -1
Otherwise, return dp[amount]
Dry Run Example
Coins = [1, 2, 5], Amount = 11
| Amount | dp value |
|---|
| 0 | 0 |
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 2 |
| 11 | 3 |
Final Answer = 3
Code Implementation
C++ Code
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
Java Code
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
Python Code
def coinChange(coins, amount):
dp = [amount + 1] * (amount + 1)
dp[0] = 0
for i in range(1, amount + 1):
for coin in coins:
if coin <= i:
dp[i] = min(dp[i], dp[i - coin] + 1)
return -1 if dp[amount] > amount else dp[amount]
Time and Space Complexity
This solution is efficient for typical interview constraints.
Important Edge Cases
These cases should always be discussed in interviews.
Common Interview Variations
Understanding this problem helps solve many DP variations.
Summary
Building secure web applications is essential for protecting users and maintaining trust. ASP.NET Core provides a comprehensive set of tools for implementing authentication, authorization, data validation, and defenses against common web attacks. By following best practices such as enforcing HTTPS, validating inputs, and securing sensitive endpoints, developers can significantly reduce security risks and build resilient applications.
Staying informed about evolving security threats and continuously applying best practices is key to long-term application security.