Sale: Get 60% Off on all Pro Plans. Buy Now

Coin Change 2

Problem

Given an integer amount and a list of coin denominations, return the number of combinations that make up the amount. You may assume that you have an infinite number of each kind of coin.

  • 0 ≤ amount ≤ 5000
  • 1 ≤ coins.length ≤ 500
  • 1 ≤ coins[i] ≤ 5000
  • All the values of coins are unique.

Example

Input: amount = 5, coins = [1, 2, 5]
Output: 4

The four combinations are: [1,1,1,1,1], [1,1,1,2], [1,2,2], and [5]. The algorithm explores two choices at each step: include the current coin (and stay at the same coin index) or skip it (move to the next coin). Memoization caches results for subproblems defined by the current coin index and accumulated amount, preventing redundant computations. For example, when considering coin 1 at amount 0, the algorithm recursively counts all combinations that sum to 5 by including or excluding coin 1, then moves on to coin 2 and so forth, accumulating counts efficiently.

Approach

Straightforward Solution

A brute-force recursive approach tries all possible combinations by exploring every coin at every step without caching, leading to exponential time complexity due to repeated subproblems.

Core Observation

The problem asks for the count of combinations to reach a target sum using unlimited coins, which naturally breaks down into overlapping subproblems defined by the current coin index and the current accumulated amount.

Path to Optimal

Preview

Recognizing overlapping subproblems and optimal substructure suggests dynamic programming. By defining a state as (coin index, current amount), the problem can be solved recursively with memoization…

Full step-by-step walkthrough on Pro

Optimal Approach

Preview

Implement a top-down recursive function with memoization that returns the number of combinations to reach the target amount starting from a given coin index and current accumulated amount. The base cases handle exact matches (return 1) and invalid states (return 0)…

Full step-by-step walkthrough on Pro

Want the full reasoning chain?

Unlock the complete walkthrough, line-by-line analysis, and recall drill.

Unlock Pro

Time

O(n * amount)

There are n coins and amount+1 possible accumulated amounts, resulting in O(n * amount) unique states. Each state is computed once and cached, so the total time is proportional to the number of states.

Space

O(n * amount)

The memoization dictionary stores results for each unique state (coin index, accumulated amount), requiring O(n * amount) auxiliary space. The recursion stack depth is at most n + amount in the worst case, but this is dominated by the memoization storage.

Pattern Spotlight

Dynamic Programming (Top-Down Memoization for Combinatorial Counting)

When counting combinations with choices that can be repeated unlimited times, model the problem as a state defined by the current position and accumulated value, and use recursion with memoization to avoid redundant exploration of identical subproblems.

Solution

Python
1class Solution:
2 def change(self, amount: int, coins: list[int]) -> int:
3 memo = {}
4
5 def dp(i, a):
6 if a == amount:
7 return 1
8 if a > amount or i == len(coins):
9 return 0
10 if (i, a) in memo:
11 return memo[(i, a)]
12
13 memo[(i, a)] = dp(i, a + coins[i]) + dp(i + 1, a)
14 return memo[(i, a)]
15
16 return dp(0, 0)

Step-by-Step Solution

1

Initialize Memoization Cache for Subproblem Results

3memo = {}

Objective

To create a dictionary that caches the number of combinations for each state defined by (coin index, accumulated amount).

Key Insight

Memoization prevents redundant recursive calls by storing results of subproblems. Since the problem has overlapping subproblems characterized by the current coin index and accumulated amount, caching these results ensures each unique state is computed once, drastically improving efficiency from exponential to polynomial time.

Interview Quick-Check

Core Logic

Memoization stores results keyed by (coin index, accumulated amount), enabling O(1) retrieval and avoiding recomputation.

Common Pitfalls & Bugs

Failing to memoize leads to exponential time due to repeated exploration of identical states.

2

Recursively Count Combinations by Including or Skipping Current Coin

To compute the number of ways to reach the target amount by either including the current coin or skipping it, using recursion with memoization.

3

Return Final Count from Initial State

To start the recursive counting process from the first coin and zero accumulated amount, returning the total number of combinations.

2 more steps with full analysis available on Pro.

Line Analysis

This solution has 5 Critical lines interviewers watch for.

Line 10 Critical
if (i, a) in memo:

Check if the current state (i, a) has been computed before.

This memoization lookup prevents recomputation of identical subproblems, drastically improving efficiency.

Line 11 Critical
return memo[(i, a)]

Return the cached result for the current state.

Returning the cached value avoids redundant recursion and ensures each state is computed once.

Line 13 Critical
memo[(i, a)] = dp(i, a + coins[i]) + dp(i + 1, a)

Compute the number of combinations by including the current coin and by skipping it, then store the sum in the memo.

This line embodies the core DP recurrence: the total combinations from the current state equal the sum of combinations including the current coin (which allows repeated use) and combinations excluding it (moving to the next coin), capturing all unique combinations without duplication.

Full line-by-line criticality + rationale for all 11 lines available on Pro.

Test Your Understanding

Why does the recursive function consider both including the current coin and moving to the next coin, rather than just one of these choices?

See the answer with Pro.

Related Problems

Dynamic Programming pattern

Don't just read it. Drill it.

Reconstruct Coin Change 2 from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Coin Change 2 drill

or drill a free problem