https://leetcode.com/problems/coin-change/ ## タグ ## 概要 > 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. ## 方針 ### Intuition 大きい金額のcoinからgreedyに選んでいけば答えが求まるかなと思ったが違った。 例) `coins = [2, 3, 5], amount = 21`のとき greedyに選ぶと5を4回使って残りの1に対応するcoinがないので払えなくなってしまう。 答えがcoinの枚数だけで枚数はいま注目しているamountより小さいamountに依存する性質があるのでDPで解けそう。 基本的なケースは `amount = 0`のときでこのときは0枚で良い。 それ以外の場合はたとえば`coins = [1, 2, 5]`のときはn-1, n-2, n-5のときの枚数にプラス1した枚数になる。 ### Top Down DP Time Complexity: $O(mn)$ ← それぞれの状態についてm回の操作が発生する Space Complexity: $O(n)$ ← 合計額の状態は0からamountまでのn + 1個ある Where: `m = len(coins), n = amount` ```python def coinChange(self, coins: List[int], amount: int) -> int: @cache def dfs(amount: int) -> int: if amount == 0: return 0 minNum = inf for coin in coins: if amount >= coin: prevStateNum = dfs(amount - coin) minNum = min(minNum, prevStateNum + 1) return minNum minNum = dfs(amount) return minNum if minNum != inf else -1 ``` ### Bottom Up DP Time Complexity: $O(mn)$ Space Complexity: $O(n)$ Where: `m = len(coins), n = amount` ```python def coinChange(self, coins: List[int], amount: int) -> int: coinNums = [inf] * (amount + 1) coinNums[0] = 0 for curAmount in range(amount + 1): for coin in coins: if curAmount >= coin: coinNums[curAmount] = min(coinNums[curAmount], coinNums[curAmount - coin] + 1) return coinNums[amount] if coinNums[amount] != inf else -1 ```