Coin change

Hero Image

Problem Overview

The Coin Change problem is a classic dynamic programming question frequently encountered in algorithm interviews. The problem statement can be described as follows:

You are given coins of different denominations and a total amount of money. Write an algorithm to compute the fewest number of coins that you need to make up that amount. If it is impossible to make up the amount with the given denominations, return -1.

Example:

Input: coins = [1, 2, 5], amount = 11
Output: 3 
Explanation: 11 = 5 + 5 + 1

Constraints:

  • 1 ≤ coins.length ≤ 12
  • 1 ≤ coins[i] ≤ 2^31 - 1
  • 0 ≤ amount ≤ 10^4

Understanding the Problem

The goal is to determine the minimum number of coins needed to make up a specified amount using the given denominations. The solution must consider all possible combinations of coins, ensuring that the minimum combination is found. One crucial observation is that we can use the technique of recursion with memoization or dynamic programming to find an efficient solution, as the problem exhibits the optimal substructure and overlapping subproblems.

Key DSA Concepts and Theory

Dynamic Programming

Dynamic Programming (DP) is a method for solving complex problems by breaking them down into simpler subproblems. It is applicable when the problem has overlapping subproblems or exhibits the property of optimal substructure. The key to solving problems via DP is to store the solutions of subproblems to avoid redundant computations.

Problem Representation

  • State Definition: Define dp[i] as the fewest number of coins required to make the amount i.
  • Base Case: dp[0] = 0, because no coins are needed to make the amount zero.
  • Recurrence Relation: For every coin c in coins and for every amount j, if j >= c, then dp[j] = min(dp[j], 1 + dp[j - c]).

Solution Approach

Step-by-Step Solution

Step 1: Initialize the DP Table

  • Create an array dp of size amount + 1 initialized to a large number (say amount + 1) to indicate that initially, all sums are unreachable.
  • Set dp[0] = 0 to represent the base case.

Step 2: Build the Solution Bottom-Up

  • Iterate over all possible coin values.
  • For each coin, iterate through potential amounts from the coin's value up to the desired amount.
  • Update the dp array using the recurrence relation.

Step 3: Return the Result

  • If dp[amount] is still a large number, return -1 indicating the amount cannot be formed. Otherwise, return dp[amount].

Code Examples

C++ Solution

#include <vector>
#include <algorithm>

class Solution {
public:
    int coinChange(std::vector<int>& coins, int amount) {
        std::vector<int> dp(amount + 1, amount + 1);
        dp[0] = 0;
        
        for (int i = 1; i <= amount; ++i) {
            for (const int& coin : coins) {
                if (i - coin >= 0) {
                    dp[i] = std::min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        
        return dp[amount] > amount ? -1 : dp[amount];
    }
};

Java Solution

import java.util.Arrays;

class Solution {
    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 (i - coin >= 0) {
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
        
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

Time and Space Complexity Analysis

  • Time Complexity: O(n * amount) where n is the number of coin denominations. Each coin tries to fill each amount up to the target once.
  • Space Complexity: O(amount), where we store at most one solution for each amount leading up to the target.

Common Mistakes to Avoid

  • Not handling the base case properly (dp[0] = 0).
  • Failing to initialize the dp array with values greater than amount to signify that these amounts are initially unreachable.
  • Confusing the state transition and incorrect indexing can lead to wrong answers or runtime errors.

Similar Problems on LeetCode

Additional Resources and References

This article should provide a thorough understanding of the Coin Change problem, including problem comprehension, solution strategy using dynamic programming, and coding implementation in both C++ and Java.