Coin Change II

Coin Change II: Counting Distinct Combinations

  • This problem involves finding the number of distinct combinations of coins that sum up to a given target amount.
  • It is a classic example of a Dynamic Programming (DP) problem, focusing on combinations rather than permutations.
Coin

Problem Description : Coin Change ||

You are given:

  1. An integer array coins where each element represents the denomination of a coin (e.g., 1, 5, 10, etc.).
  2. An integer amount representing the target sum.

Objective: Return the number of distinct combinations of coins that sum up to amount. If it’s impossible to reach the amount, return 0.

Problem Breakdown

  1. Input: 2D grid of non-negative integers.
  2. Objective: Find the length of the longest strictly increasing path.
  3. Movement: Can move to adjacent cells (up, down, left, right) but not diagonally.
  4. Constraints: Each move must go to a cell with a strictly greater value.
  5. Goal: Determine the longest path by efficiently exploring the grid.

Explanation:

  • 1+1+1+1 = 4
  • 1+1+2 = 4
  • 2+2 = 4
  • 1+3 = 4

Constraints: 

  • 1 <= coins[i] <= 1000
  • 1 <= coins.length <= 100
  • 0 <= amount <= 1000

There are mainly three approach to solve this problem – 

  1. Recursion
  2. Dynamic Programming (Top-Down)
  3. Dynamic Programming (Bottom-up)

1. Recursion

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        coins.sort()
        memo = [[-1] * (amount + 1) for _ in range(len(coins) + 1)]

        def dfs(i, a):
            if a == 0:
                return 1
            if i >= len(coins):
                return 0
            if memo[i][a] != -1:
                return memo[i][a]
            
            res = 0
            if a >= coins[i]:
                res = dfs(i + 1, a)
                res += dfs(i, a - coins[i])

            memo[i][a] = res
            return res

        return dfs(0, amount)

2. Dynamic Programming (Top-Down)

Time & Space Complexity
  • Time complexity: O(n^3)
  • Space complexity: O(n^2)
class Solution:
    def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
        ROWS, COLS = len(matrix), len(matrix[0])
        dp = {}  # (r, c) -> LIP

        def dfs(r, c, prevVal):
            if (r < 0 or r == ROWS or c < 0 or 
                c == COLS or matrix[r][c] <= prevVal
            ):
                return 0
            if (r, c) in dp:
                return dp[(r, c)]

            res = 1
            res = max(res, 1 + dfs(r + 1, c, matrix[r][c]))
            res = max(res, 1 + dfs(r - 1, c, matrix[r][c]))
            res = max(res, 1 + dfs(r, c + 1, matrix[r][c]))
            res = max(res, 1 + dfs(r, c - 1, matrix[r][c]))
            dp[(r, c)] = res
            return res

        for r in range(ROWS):
            for c in range(COLS):
                dfs(r, c, -1)
        return max(dp.values())

3. Dynamic Programming (Bottom-Up)

Time & Space Complexity
  • Time complexity: O(m∗n)
  • Space complexity: O(m∗n)
class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        n = len(coins)
        coins.sort()
        dp = [[0] * (amount + 1) for _ in range(n + 1)]
        
        for i in range(n + 1):
            dp[i][0] = 1
        
        for i in range(n - 1, -1, -1):
            for a in range(amount + 1):
                if a >= coins[i]:
                    dp[i][a] = dp[i + 1][a]  
                    dp[i][a] += dp[i][a - coins[i]]  

        return dp[0][amount]

More Articles