Problem of The Day: Coin Change
Problem Statement
My Solution [Accepted]
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
if amount == 0:
return 0
N = len(coins)
@cache
def dp(i, curr_sum):
if curr_sum == amount:
return 0
if curr_sum > amount or i == N:
return float('inf')
res = min(
dp(i + 1, curr_sum),
dp(i, curr_sum + coins[i]) + 1,
)
return res
ans = dp(0, 0)
if ans == float('inf'):
return -1
return ans
Convert to bottom-up approach (forward)
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
N = len(coins)
INF = float('inf')
# dp[i][s] = answer to dp(i, s)
dp = [[INF] * (amount + 1) for _ in range(N + 1)]
# Base case: if current sum == amount → 0 coins needed
for i in range(N + 1):
dp[i][amount] = 0
# Fill table
# Because dp[i][s] depends on:
# dp[i + 1][s] (row below)
# dp[i][s + coin] (to the right)
# We iterate i backwards and s backwards.
for i in range(N - 1, -1, -1):
for s in range(amount - 1, -1, -1):
# Skip current coin
skip = dp[i + 1][s]
# Take current coin
take = INF
if s + coins[i] <= amount:
take = 1 + dp[i][s + coins[i]]
dp[i][s] = min(skip, take)
ans = dp[0][0]
return ans if ans != INF else -1
Convert to bottom-up approach (backward)
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
N = len(coins)
INF = float('inf')
# dp[i][s] = min coins to make sum s using first i coins
dp = [[INF] * (amount + 1) for _ in range(N + 1)]
# Base case: 0 coins needed to make sum 0
for i in range(N + 1):
dp[i][0] = 0
for i in range(1, N + 1):
coin = coins[i - 1]
for s in range(1, amount + 1):
# Option 1: don't use this coin
dp[i][s] = dp[i - 1][s]
# Option 2: use this coin (unbounded, so stay in same row)
if s >= coin:
dp[i][s] = min(dp[i][s],
1 + dp[i][s - coin])
return dp[N][amount] if dp[N][amount] != INF else -1
Editorial
Approach 2 (Dynamic programming - Top down) [Accepted]
from functools import lru_cache
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
@lru_cache(None)
def dfs(rem):
if rem < 0:
return -1
if rem == 0:
return 0
min_cost = float('inf')
for coin in coins:
res = dfs(rem - coin)
if res != -1:
min_cost = min(min_cost, res + 1)
return min_cost if min_cost != float('inf') else -1
return dfs(amount)
Approach 3 (Dynamic programming - Bottom up) [Accepted]
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for x in range(coin, amount + 1):
dp[x] = min(dp[x], dp[x - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1