Coin Change
Compute the fewest number of coins needed to make up a given amount. The quintessential problem for learning bottom-up dynamic programming tabulation and overlapping subproblems.
Problem Statement
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.
Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
Examples & Constraints
- 1 <= coins.length <= 12
- 1 <= coins[i] <= 2^31 - 1
- 0 <= amount <= 10^4
Bottom-Up Dynamic Programming (Tabulation)
Optimal ApproachBuild an array dp where dp[i] represents the minimum coins needed for amount i. For each amount, test all coins and pick the minimum of 1 + dp[i - coin] in O(amount * len(coins)) time.
Where S is the target amount and N is the number of coin denominations. The outer loop runs S times, and the inner loop checks N coins.
Consumes an array dp of size amount + 1 to store subproblem solutions.
Underlying algorithmic logic
Why Greedy Fails for Arbitrary Coins
Humans naturally try to pick the largest coin first (Greedy approach).
For example, with coins [1, 3, 4] and amount 6:
- Greedy picks
4then1then1= 3 coins (4 + 1 + 1). - But the optimal combination is
3 + 3= 2 coins!
Because greedy choices do not guarantee the global optimum for arbitrary denominations, we must evaluate overlapping subproblems using Dynamic Programming.
The Recurrence Relation:
Let dp[a] be the minimum coins needed to make amount a.
- Base Case:
dp[0] = 0(zero coins needed to form amount 0). - Initialize all other entries
dp[1..amount]with an infinity sentinel (amount + 1). - For each amount
afrom1toamount:
- For each coin
cincoins: - If
a - c >= 0:dp[a] = min(dp[a], 1 + dp[a - c])
- If
dp[amount] > amount, the target cannot be formed; return-1. Otherwise, returndp[amount].
Step-by-step execution walkthrough
dp of size 12 initialized. dp[0] = 0, all others initialized to infinity (12).
dp[1] = 1 (using coin 1). dp[2] = min(1+dp[1], 1+dp[0]) = 1 (using coin 2).
dp[5] = 1 (using coin 5). dp[6] = 1 + dp[6 - 5] = 1 + dp[1] = 2.
dp[11] = 1 + dp[11 - 5] = 1 + dp[6] = 1 + 2 = 3 coins (5 + 5 + 1).
- •Optimal O(S * n) runtime
- •Clean bottom-up array iteration avoiding recursion overhead
- •Consumes O(S) auxiliary memory proportional to the target amount
Code Implementations
Ready-to-useCopy & paste production-ready solutions for Bottom-Up Dynamic Programming (Tabulation) in your language of choice
def coin_change(coins: list[int], amount: int) -> int:dp = [amount + 1] * (amount + 1)dp[0] = 0for a in range(1, amount + 1):for c in coins:if a - c >= 0:dp[a] = min(dp[a], 1 + dp[a - c])return dp[amount] if dp[amount] <= amount else -1if __name__ == "__main__":print("test1 (11):", coin_change([1, 2, 5], 11))print("test2 (3):", coin_change([2], 3))print("test3 (0):", coin_change([1], 0))
Understand Complexity Analysis Visually
Analyse algorithm time and space efficiencies using Big O boundary notations. Test live edge cases and watch memory state transitions step-by-step.
Frequently Asked Questions
Related Logic & Algorithm Problems
Climbing Stairs
Count the distinct ways to reach the top of an n-step staircase when hopping 1 or 2 steps at a time. The premier gentle introduction to Dynamic Programming and state transition.
Maximum Subarray (Kadane's Algorithm)
Find the contiguous slice of an array that yields the largest sum. Kadane's algorithm demonstrates the art of shedding negative historical baggage in O(n) linear time.