You are given coins of different denominations and a total amount of money amount. Write a function to compute 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.
 
Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:
Input: coins = [2], amount = 3
Output: -1

Example 3:
Input: coins = [1], amount = 0
Output: 0

Example 4:
Input: coins = [1], amount = 1
Output: 1

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

 
Constraints:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

Difficulty: interview

Starter code:

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        

Solutions:
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        n, res = len(coins), amount + 1

        def dfs(index, target, cnt):
            nonlocal res
            if cnt + (target + coins[index] - 1) // coins[index] >= res:
                return

            if target % coins[index] == 0:
                res = cnt + target // coins[index]
                return

            if index == n - 1:
                return

            for i in range(target // coins[index], -1, -1):
                dfs(index + 1, target - coins[index] * i, cnt + i)

        dfs(0, amount, 0)
        return -1 if res > amount else res

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        elif amount == 0:
            return 0
        
        coins.sort(reverse = True)
        visited = set()
        q = collections.deque([])
        for c in coins:
            if c == amount:
                return 1
            elif c < amount:
                q.append(c)
                visited.add(c)
            
        count = 1
        while q:
            size = len(q)
            count += 1
            for _ in range(size):
                prev = q.popleft()
                for c in coins:
                    cur = prev + c
                    if cur == amount:
                        return count
                    elif cur < amount and cur not in visited:
                        visited.add(cur)
                        q.append(cur)
        
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        INVALID = 2**32
        dp = [INVALID] * (amount + 1)
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount + 1):
                if dp[i - coin] >= dp[i]: continue
                dp[i] = dp[i - coin] + 1
        return -1 if dp[amount] == INVALID else dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[amount, 0]]
        visited = {amount}
        for q in queue:
            for c in coins:
                if q[0] - c in visited: continue
                if q[0] == c: return q[1] + 1
                if q[0] > c:
                    visited.add(q[0] - c)
                    queue.append([q[0] - c, q[1] + 1])
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if not amount:  # Don't need any coin.
            return 0

        queue = deque([(0, 0)])
        visited = [True] + [False] * amount
        while queue:
            totalCoins, currVal = queue.popleft()
            totalCoins += 1  # Take a new coin.
            for coin in coins:
                nextVal = currVal + coin
                if nextVal == amount:  # Find a combination.
                    return totalCoins

                if nextVal < amount:  # Could add more coins.
                    if not visited[nextVal]:  # Current value not checked.
                        visited[nextVal] = True  # Prevent checking again.
                        queue.append((totalCoins, nextVal))

        return -1  # Cannot find any combination.

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[amount, 0]]
        visited = set()
        for q in queue:
            for c in coins:
                if q[0] - c in visited: continue
                if q[0] == c: return q[1] + 1
                if q[0] > c:
                    visited.add(q[0] - c)
                    queue.append([q[0] - c, q[1] + 1])
        return -1

from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        
        if not coins:
            return -1
        queue = deque([])
        for coin in coins:
            if amount-coin>=0:
                queue.append((1, amount-coin))
        
        seen = set()
        while queue:
            # print(queue)
            nex = queue.popleft()
            if nex[1] == 0:
                return nex[0]
            
            for coin in coins:
                if nex[1] - coin not in seen and nex[1]-coin>=0:
                    queue.append((nex[0]+1, nex[1]-coin))
                    seen.add(nex[1]-coin)
        
        return -1
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        for coin in coins:
            for a in range(coin, amount + 1):
                if dp[a - coin] >= dp[a]:
                    continue
                dp[a] = dp[a - coin] + 1
        return dp[amount] if dp[amount] < float('inf') else -1


from collections import deque

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        queue = deque()
        visited = set()
        queue.append((amount, 0))
        answer = -1
        while queue:
            target, numCoins = queue.popleft()
            if target == 0:
                answer = numCoins
                break
            elif target > 0:
                for coin in coins:
                    if (target - coin) not in visited:
                        visited.add(target - coin)
                        queue.append((target - coin, numCoins + 1))
        return answer

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        res = 0
        queue = collections.deque([(amount, 0)])
        seen = {amount}
        while queue:
            head, res = queue.popleft()
            if head == 0:
                return res
            for c in coins:
                new_amount = head - c
                if new_amount > -1 and new_amount not in seen:
                    queue.append((new_amount, res+1))
                    seen.add(new_amount)
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if not coins or amount<0: return -1
        if amount ==0: return 0
        
        q = [(amount, 0)]
        visited = {0}
        coins.sort(reverse=True)
        while q:
            node, depth = q.pop(0)
            
            for coin in coins:
                rest = node - coin
                if rest == 0:
                    return depth + 1
                
                if rest>0 and rest not in visited:
                    q.append((rest, depth+1))
                    visited.add(rest)
                    
        return -1


from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        visited = {0}
        tempQueue = deque()
        tempQueue.append((0, 0))
        while tempQueue:
            currentVal, currentCount = tempQueue.popleft()
            for coin in coins:
                nextVal = currentVal + coin
                if nextVal == amount:
                    return currentCount + 1
                elif nextVal < amount:
                    if nextVal not in visited:
                        visited.add(nextVal)
                        tempQueue.append((nextVal, currentCount + 1))
        return -1

class Solution:
    def coinChange(self, coins, amount):
        coins.sort(reverse=True)
        queue = [(amount, 0)]
        visited = [False for i in range(amount+1)]
        for val, cnt in queue:
            if val == 0:
                return cnt
            for coin in coins:
                if val - coin >= 0 and not visited[val-coin]:
                    visited[val-coin] = True
                    queue.append((val-coin, cnt+1))
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        #coinValues = collections.defaultdict(list)
        for coin in coins:
            for i in range(coin, amount+1):
                if dp[i-coin] + 1 < dp[i]:
                    dp[i] = dp[i-coin] + 1
                    #coinValues[i] = coinValues[i-coin] + [coin]
        #print(coinValues[amount])
        return dp[-1] if dp[-1] != float('inf') else -1

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 
    
    
        queue = deque([])
        seen = set()
        coins = sorted(coins, reverse=True)

        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1
#         coins = sorted(coins, reverse=True)
#         memo = {}
#         def coinChangeRec(index, amount):
#             if amount == 0:
#                 return 0
#             if amount < 0 or index == len(coins):
#                 return math.inf
#             if (index, amount) in memo:
#                 return memo[(index, amount)]
            
#             withCoin = coinChangeRec(index, amount - coins[index]) + 1
#             withoutCoin = coinChangeRec(index+1, amount)
            
#             memo[(index, amount)] = min(withCoin, withoutCoin)
#             return min(withCoin, withoutCoin)
                     
#         minCoins = coinChangeRec(0, amount)
#         if minCoins == math.inf:
#             return -1
#         return minCoins
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        queue = deque([])
        seen = set()
        
        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1
#         coins = sorted(coins, reverse=True)
#         memo = {}
#         def coinChangeRec(index, amount):
#             if amount == 0:
#                 return 0
#             if amount < 0 or index == len(coins):
#                 return math.inf
#             if (index, amount) in memo:
#                 return memo[(index, amount)]
            
#             withCoin = coinChangeRec(index, amount - coins[index]) + 1
#             withoutCoin = coinChangeRec(index+1, amount)
            
#             memo[(index, amount)] = min(withCoin, withoutCoin)
#             return min(withCoin, withoutCoin)
                     
#         minCoins = coinChangeRec(0, amount)
#         if minCoins == math.inf:
#             return -1
#         return minCoins
    


class Solution:
    def coinChange(self, coins, amount):
        if not coins: return -1
        if not amount: return 0
        if amount in coins: return 1
        
        C = tuple(sorted(coins,reverse=True))
        length, self.ans = len(C)-1, float('inf')
        
        def find(coin, step, target):
            now_coin = C[coin]
            q, r = divmod(target,now_coin)
            
            if not r:
                self.ans = min(self.ans, step+q)
            elif coin < length and step+q+1 < self.ans:
                coin += 1
                for j in range(q,-1,-1):
                    find(coin, step+j, target-j*now_coin)
            
        find(0, 0, amount)
        return -1 if self.ans==float('inf') else self.ans

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 
    
    
        queue = deque([])
        seen = set()
        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1



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 
    
    
        queue = deque([])
        seen = set()
        
        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1
#         coins = sorted(coins, reverse=True)
#         memo = {}
#         def coinChangeRec(index, amount):
#             if amount == 0:
#                 return 0
#             if amount < 0 or index == len(coins):
#                 return math.inf
#             if (index, amount) in memo:
#                 return memo[(index, amount)]
            
#             withCoin = coinChangeRec(index, amount - coins[index]) + 1
#             withoutCoin = coinChangeRec(index+1, amount)
            
#             memo[(index, amount)] = min(withCoin, withoutCoin)
#             return min(withCoin, withoutCoin)
                     
#         minCoins = coinChangeRec(0, amount)
#         if minCoins == math.inf:
#             return -1
#         return minCoins
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        INVALID = float('inf')
        dp = [INVALID] * (amount + 1)
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount + 1):
                if dp[i - coin] >= dp[i]: continue
                dp[i] = dp[i - coin] + 1
        return -1 if dp[amount] == INVALID else dp[amount]

        # # sol 2 dp bottom up
        # INVALID = float('inf')
        # dp = [INVALID] * (amount + 1)
        # dp[0] = 0
        # for i in range(amount + 1):
        #     for coin in coins:
        #         remains = i - coin
        #         if remains < 0: continue
        #         dp[i] = min(dp[i], dp[remains] + 1)
        # return -1 if dp[amount] == INVALID else dp[amount]

        # # sol 1 dp with memo
        # memo = {}
        # def dp(n):
        #     if n in memo: return memo[n]
        #     if n < 0: return -1
        #     if n == 0: return 0
        #     res = float('inf')
        #     for coin in coins:
        #         sub = dp(n - coin)
        #         if sub == -1: continue
        #         res = min(res, 1 + sub)
        #     memo[n] = res if res != float('inf') else -1
        #     return memo[n]
        # return dp(amount)



import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0]*(amount+1)
        dp[0] = 0
        for i in range(1,amount+1):
            min = math.inf 
            for j in coins:
                if i-j>=0 and dp[i-j]<min:
                    min = dp[i-j]
            dp[i] = min+1
        #print(dp)
        return dp[-1] if dp[-1]!=math.inf else -1
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        numpath = [0] + [float('inf')] * (amount) # 가장 큰 값으로 amount + 1만큼 배열을 초기화
        
        for coin in coins:
            for i in range(coin, amount + 1):
                # 처음 도착한 위치: numpath[i] = inf, numpath[i - coin] + 1 = inf
                # 처음 도착한 곳이 아닌 위치: numpath[i] = 이전까지 이 위치에 올 수 있었던 최소 동전 개수
                # numpath[i - coin] + 1 = 이번 coin만큼 건너띄기 전의 값에 + 1을 해준 최소 동전 개수 
                if numpath[i] > 1 + numpath[i - coin]:
                    numpath[i] = 1 + numpath[i - coin]
                
        return numpath[amount] if numpath[amount] != float('inf') else -1 # amount가 inf가 아닌 경우에 최소 동전 개수가 nunpath[amount]에 저장

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

remain =11
fewest =inf
initially visited = (), q = [(11,1)] --count is 1 because of off-by-one (see code below)

after first while-iteration:
    q [(10,1),(9,1),(6,1)]
    visited(10,9,6)
    
----ALGO-----
BFS:
q = [(remain,count),(remain,count),...]
visited =set(amount_explored/to_explore, amt2, amt3) 
^ purpose: so we won't try to go down a path if that path (same remain target) has already been explored in another path

----BIG-O----
O(V+E) -- O(remain+nextRemain)?

'''
class Solution:
    from collections import deque
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount <= 0:
            return 0
        coins = set(coins)
        count = 1
        q = deque()
        q.append((amount,count)) 
        visited = set()
        visited.add(amount)
        
        while q:
            remain,count = q.popleft()
            if remain in coins:
                return count
            for c in coins:
                if remain-c>0 and (remain-c) not in visited:
                    q.append((remain-c,count+1))
                    visited.add(remain-c) #to avoid going down this path if another path has already explored this
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
       
        
        def bfs():# bfs > dfs 
            q = [(amount, 0)]

            if (amount == 0):
                return 0

            seen = set()
            
            coins.sort(reverse=True)
            print(coins)
            while len(q) > 0:
                
                a, ci = q.pop(0)
                if a in seen:
                    continue
                else:
                    seen.add(a)
                
                for c in coins:
                    if a-c == 0:
                        return ci+1 
                    if a-c > 0:
                        #print(a-c)
                        #print(ci+1)
                        q.append( (a-c, ci+1) )

            return -1
        
        def dfs():
            if (amount == 0):
                return 0
            
            coins.sort()
            
            s = [ (amount, 0) ] 
            
            while len(s) > 0:
                a, ci = s.pop()
                
                for c in coins:
                    if a-c == 0:
                        return ci+1 
                    if a-c > 0:
                        #print(a-c)
                        #print(ci+1)
                        s.append( (a-c, ci+1) )
                
                
            return -1
        
        def paste() -> int:
            queue = deque([])
            seen = set()

            queue.append((0,0))

            while queue:
                summ, num_coins = queue.popleft()
                if summ == amount:
                    return num_coins

                for coin in coins:
                    if summ+coin<=amount and summ+coin not in seen:
                        queue.append((summ+coin, num_coins+1))
                        seen.add(summ+coin)

            return -1
        return paste()
        #return bfs()
        #return dfs()


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        Visited = {amount: 0}
        coins.sort(reverse = True)
        queue = deque()
        queue.append(amount)
        while queue:
            curr = queue.popleft()
            for coin in coins:
                if curr - coin >= 0 and curr - coin not in Visited:
                    queue.append(curr - coin)
                    Visited[curr - coin] = Visited[curr] + 1
                if 0 in Visited:
                    return Visited[0]
        return -1
                
        


from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        q = deque()
        q.append([amount,0])
        level = 0
        seen = set()
        while q:
            amt, level = q.popleft()
            for n in coins:
                if amt - n == 0:
                    return level + 1
                if amt - n in seen or amt - n < 0:
                    continue
                seen.add(amt-n)
                q.append([amt-n, level+1])
                
        return -1

'''
coins = [2, 7, 10]
        [4, 9, 12] [9, 14, 17] [12, 17, 20]
amount = 14
'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
        coins.sort()
        visited = {}
        sums = []
        for coin in coins:
            if coin == amount:
                return 1
            visited[coin] = 1
            sums.append(coin)
        
        q = []
        q.append((1, sums))
        while q:
            count, sums = q.pop()
            next_sums = []
            for coin in coins:
                for s in sums:
                    current = coin + s
                    if current < amount and current not in visited:
                        visited[current] = 1
                        next_sums.append(current)
                    elif current == amount:
                        return count + 1
                    else:
                        visited[current] = 1
            if next_sums:
                q.insert(0, (count + 1, next_sums))
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        if amount == 0:
            return 0
        elif amount < 0:
            return -1
        else:
            arr = []
            for i in coins:
                rv = self.coinChange(coins, amount - i)
                if rv != -1:
                    arr.append(rv)
            if not arr:
                return -1
            else:
                return min(arr) + 1
        '''
        dp = [amount+1 for i in range(amount+1)]
        dp[0] = 0
        for i in range(amount+1):
            for c in coins:
                if c <= i and dp[i] > dp[i-c] + 1:
                    dp[i] = dp[i-c] + 1
        return -1 if dp[amount] > amount else dp[amount]

from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        queue = deque([])
        seen = set()
        
        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1

from collections import deque

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        This a DP problem
        but we can solve this using BFS
        https://leetcode.com/problems/coin-change/discuss/77361/Fast-Python-BFS-Solution
        '''
        
        if not amount:
            return 0
        
        queue = deque([(amount, 0)])
        visited = set([amount])
        while queue:
            remaining, steps = queue.popleft()
            
            for coin in coins:
                if coin == remaining:
                    return steps + 1
                elif coin < remaining:
                    temp = remaining - coin
                    if temp > 0 and temp not in visited:
                        visited.add(temp)
                        queue.append((temp, steps + 1))
        return -1

from collections import deque

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        queue = deque()
        visited = [False] * amount
        queue.append((amount, 0))
        answer = -1
        while queue:
            target, numCoins = queue.popleft()
            if target == 0:
                answer = numCoins
                break
            elif target > 0:
                for coin in coins:
                    if target - coin >= 0 and not visited[target - coin]:
                        visited[target - coin] = True
                        queue.append((target - coin, numCoins + 1))
        return answer

from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        queue = deque([])
        seen = set()
        
        queue.append((0,0))
        
        while queue:
            summ, num_coins = queue.popleft()
            if summ == amount:
                return num_coins
            
            for coin in coins:
                if summ+coin<=amount and summ+coin not in seen:
                    queue.append((summ+coin, num_coins+1))
                    seen.add(summ+coin)
        
        return -1
                    
                


from collections import defaultdict
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        MAX = float('inf')
        dp = defaultdict(lambda: MAX)
        dp[0] = 0
        for i in range(1, amount+1):
            dp[i] = 1 + min([dp[i-c] for c in coins])
        return -1 if dp[amount] == MAX else dp[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        l = [int(amount+1)] * int(amount+1)
        if amount == 0:
            return 0
        else:
            l[0] = 0
            # sort coins as an optimization
            coins.sort()
            for i in range(1, amount + 1):
                for coin in coins:
                    remainder = i - coin
                    if remainder < 0:
                        break
                    else:
                        if l[i] > l[remainder] + 1:
                            l[i] = l[remainder] + 1
        if l[amount] == amount + 1:
            return -1
        else:
            return l[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        coins.sort(reverse=True)
        queue = [amount]
        visit = {amount: 0}
        while queue:
            remain = queue.pop(0)
            for c in coins:
                branch = remain - c
                if 0 <= branch not in visit:
                    queue.append(branch)
                    visit.update({branch: visit[remain] + 1})
        return visit.get(0) or -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount ==0:return 0
        visited = set([amount])
        candidates = [amount]
        res = 0
        while candidates:
            res += 1
            next_candidates = []
            for candidate in candidates:
                for coin in coins:
                    if candidate-coin ==0:
                        return res
                    elif candidate-coin >0 and candidate-coin not in visited:
                        next_candidates.append(candidate- coin)
                        visited.add(candidate-coin)
            candidates = next_candidates
            
        return -1

class Solution:
    def coinChange(self, denoms: List[int], n: int) -> int:  
        if n == 0:
            return 0
        nodes = set([0])
        seen = set()
        count = 1
        while nodes:
            seen = seen | nodes
            next_nodes = set()
            for node in nodes:
                for denom in denoms:
                    candidate = node + denom
                    if candidate == n:
                        return count
                    elif candidate < n and candidate not in seen:
                        next_nodes.add(candidate)
            count += 1
            nodes = next_nodes

        return -1
        pass

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        l = [int(amount+1)] * int(amount+1)
        if amount == 0:
            return 0
        else:
            l[0] = 0
            for i in range(1, amount + 1):
                for coin in coins:
                    remainder = i - coin
                    if remainder < 0:
                        pass
                    else:
                        if l[i] > l[remainder] + 1:
                            l[i] = l[remainder] + 1
        if l[amount] == amount + 1:
            return -1
        else:
            return l[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        d = [0] + [float('inf')] * amount
        for v in range(1, len(d)):
            for c in coins:
                if c <= v and d[v] > (d[v-c] + 1):
                    d[v] = d[v-c] + 1
        return d[-1] if d[-1] < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        queue = deque()
        numCoins = 0
        seen = set()
        coins.sort(reverse=True)
        queue.append(amount)
        
        if amount == 0:
            return 0
        
        while queue:
            qlen = len(queue)
            numCoins += 1
            for i in range(qlen):
                x = queue.popleft()
                for coin in coins:
                    if x - coin == 0:
                        return numCoins
                    elif x - coin > 0:
                        if x-coin not in seen:
                            queue.append(x-coin)
                            seen.add(x-coin)
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        result = -1

        def helper(coins: List[int], pos: int, left: int, current: int):
            if left % coins[pos] == 0:
                nonlocal result
                if result == -1 or result > current + left // coins[pos]:
                    result = current + left // coins[pos]
                return

            if pos == len(coins) - 1:
                return

            for k in range(left // coins[pos], -1, -1):
                new_amount = left - coins[pos] * k
                new_count = current + k
                if result != -1 and result < new_count + (new_amount + coins[pos + 1] - 1) / coins[pos + 1]:
                    break
                helper(coins, pos + 1, left - coins[pos] * k, current + k)

        helper(coins, 0, amount, 0)
        return result


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount: return 0
        queue = collections.deque([(0,0)])
        seen = set()
        while queue:
            curAmount,step = queue.popleft()
            if curAmount == amount:
                return step
            for j in range(len(coins)):
                newAmount = curAmount+coins[j]
                if newAmount <= amount and newAmount not in seen:
                    seen.add(newAmount)
                    queue.append((newAmount,step+1))
                    
        return -1


class Solution:
    # def coinChange(self, coins: List[int], amount: int) -> int:
    #     if amount == 0:
    #         return 0
    #     coins.sort(reverse = True)
    #     m = amount + 1
    #     store = [float('inf')] * m
    #     store[0] = 0
    #     for i in range(len(coins)):
    #         x = self.countChange(coins, amount, i, store)
    #         if x > -1:
    #             m = min(m, x)
    #     return m if m != amount + 1 else -1
    
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        store = [float('inf')] * (amount + 1)
        store[0] = 0
        for i in range(amount+1):
            for coin in coins:
                if i-coin >= 0 and store[i] > store[i-coin] + 1:
                    store[i] = store[i-coin] + 1
        return store[amount] if store[amount] != float('inf') else -1
    

    
    # def countChange(self, coins: List[int], amount: int, index: int, store) -> int:
    #     if amount == 0:
    #         return 0
    #     elif store[amount] != float('inf'):
    #         return store[amount]
    #     elif index >= len(coins):
    #         return -1
    #     elif amount < coins[index]:
    #         return -1
    #     m = float('inf')
    #     for i in range(1, amount // coins[index] + 1):
    #         for j in range(len(coins)+1):
    #             x = self.countChange(coins, amount - coins[index] * i, j, store)
    #             if x > -1:
    #                 m = min(m, x + i)
    #     store[amount] = m
    #     return m if m != float('inf') else -1
        
        
    # def countChange(self, coins: List[int], amount: int, index: int) -> int:
    #     if index >= len(coins):
    #         return 0
    #     elif amount < coins[index]:
    #         return 0
    #     # num = amount // coins[index]
    #     num = 1
    #     print(coins, amount, index, num)
    #     amount -= coins[index]
    #     if amount == 0:
    #         return num
    #     for i in range(index, len(coins)):
    #         x = self.countChange(coins, amount, i)
    #         if x > 0:
    #             return x + num
    #     else:
    #         return 0


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
        coins.sort()
        coins_set=set(coins)
        if amount in coins_set:
            return 1
        queue=deque([[amount,0]])
        seen=set()
        processed=set()
        while queue:
            rem,count=queue.popleft()
            if rem in seen:
                continue
            seen.add(rem)
            #print(rem)
            for coin in coins:
                new_rem=rem-coin
                if new_rem==0:
                    return count+1
                if new_rem>0:
                    if new_rem in coins_set:
                        return count+2
                    if new_rem not in seen and new_rem not in processed:
                        queue.append([new_rem,count+1])
                        processed.add(new_rem)
        return -1
    
            
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
#         if not amount:
#             return 0
        

        
#         coins.sort()
        
#         coinsSet=set(coins)
        
#         if amount in coins:
#             return 1
        
#         currLevel=[amount]
#         nextLevel=[]
#         level=1
        
#         seen=set()
        
#         while currLevel:
            
#             while currLevel:
#                 node=currLevel.pop(0)
                
#                 for coin in coins:
#                     temp=node-coin
#                     if temp<0:
#                         break
#                     if temp not in seen:
#                         if temp in coinsSet:
#                             return level+1
#                         else:
#                             nextLevel.append(temp)
#                             seen.add(temp)
#             level+=1
#             currLevel=nextLevel
#             nextLevel=[]
#         return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('+inf')] * (amount + 1)
        dp[0] = 0
        
        for cnt in range(1, amount + 1):
            for coin in coins:
                if cnt - coin >= 0 and dp[cnt - coin] + 1 < dp[cnt]:
                    dp[cnt] = dp[cnt - coin] + 1

        if dp[amount] == float('+inf'):
            return -1
        
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for _ in range(amount + 1)]
        
        dp[0] = 0
        
        for x in range(len(dp)):
            for coin in coins:
                if x < coin:
                    continue
                if dp[x - coin] + 1 < dp[x]:
                    dp[x] = dp[x - coin] + 1
        if dp[amount] == float('inf'):
            return -1
        else:
            return dp[x]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        rec = [0]*(amount)
        fr = [0]
        new = []
        l=1
        while fr or new:
            if not fr:
                fr = new
                new = []
                l+=1
            cur = fr.pop(0)
            for c in coins:
                if cur+c == amount:
                    return l
                if cur+c < amount and not rec[cur+c]:
                    new.append(cur+c)
                    rec[cur+c] = 1
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = [float('inf') for _ in range(amount+1)]
        memo[0] = 0
        for i in range(1, len(memo)):
            minv = float('inf')
            for coin in coins:
                if i-coin >= 0 and memo[i-coin] < minv:
                    minv = memo[i-coin]
            memo[i] = 1 + minv
        #print(memo)
        return memo[amount] if memo[amount] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        cache = [0]*(amount+1)
        for ind in range(1, amount + 1):
            min_coins = float('inf')
        
            for coin in coins:
                if ind - coin >= 0:
                    coins_needed = cache[ind - coin] + 1
                    if coins_needed < min_coins:
                        min_coins = coins_needed
            cache[ind] = min_coins
        # print(cache)
        if cache[amount] == float('inf'):
            return -1
        return cache[amount]
                    


class Solution:
    def coinChange(self, coins: [int], amount: int) -> int:
        summary = 0
        result = [float('inf')]*(amount+1)
        result[0] = 0
        while summary <= amount:
            if result[summary] != -1:
                for i in coins:
                    if summary + i <= amount and result[summary] + 1 < result[summary + i]:
                        result[summary + i] = result[summary] + 1
            summary += 1
        return -1 if result[amount] == float('inf') else result[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        arr = [float('inf')] * (amount+1) 
        arr[0] = 0
        
        for i in range(amount+1):
            for c in coins:
                if i+c > amount:
                    continue
                                
                if arr[i+c] > arr[i] + 1:
                    arr[i+c] = arr[i] + 1
                    
        
        if arr[-1] == float('inf'):
            return -1
        
        return arr[-1]
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        
        min_coins = [float('inf') for x in range(amount + 1)]
        
        min_coins[0] = 0
        
        for value in range(1, amount+1):
            for coin in coins:
                if coin <= value:
                    if min_coins[value-coin] + 1 < min_coins[value] :
                        min_coins[value] = min_coins[value-coin] + 1
        
        if min_coins[amount] == float('inf'):
            return -1
        return min_coins[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[amount, 0]]
        visited = {0}
        for q in queue:
            for c in coins:
                if q[0] == c:
                    return q[1] + 1
                elif q[0] > c and q[0] - c not in visited:
                    visited.add(q[0] - c)
                    queue.append([q[0]-c, q[1] + 1])
        return -1
                 
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        m = [-1 for x in range(amount+1)]
        m[0] = 0
        coins = sorted(coins)
        if amount < min(coins):
            return m[amount]
        m[coins[0]] = 1
        for x in range(coins[0], amount+1):
            current_min = 10000000
            for y in coins:
                if y > x:
                    continue
                if m[x-y] < current_min and m[x-y] != -1:
                    current_min = m[x-y]
            if current_min == 10000000:
                m[x] = -1
            else:
                m[x] = current_min + 1
        print(m)
        return m[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[0]*(amount+1)
        for i in range(1,len(dp)):
            dp[i]=float('inf')
            for j in coins:
                if i>=j and dp[i-j]+1<dp[i]:
                    dp[i]=dp[i-j]+1
        return -1 if dp[amount]==float('inf') else dp[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1] * (amount+1)
        dp[0] = 0
        for i in range(amount+1):
            for n in coins:
                if i - n >= 0 and dp[i-n] + 1 < dp[i]:
                    dp[i] = dp[i-n] + 1
        return dp[-1] if dp[-1] != amount + 1 else -1

from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        queue = deque()
        queue.append((amount,0))
        visited = set()
        while queue:
            remainder, level = queue.popleft()
            for coin in coins:
                if remainder - coin == 0:
                    return level+1
                
                elif remainder - coin > 0 and (remainder - coin) not in visited:
                    queue.append((remainder - coin,level+1))
                    visited.add(remainder-coin)
        
        return -1 

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        # Sol.3 - DP (Bottom up)
        dp = [amount+1] * (amount+1)
        dp[0] = 0
        for i in range(amount+1):
            for coin in coins:
                if coin <= i and (dp[i] > dp[i-coin]+1):
                    dp[i] = dp[i-coin] + 1

        return dp[amount] if dp[amount] != (amount + 1) else -1
    
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        table = [0] + [amount+1 for _ in range(amount)]
        for i in range(1,amount+1):
            for coin in coins:
                if coin<= i:
                    sub_result = table[i-coin]
                    if sub_result +1 < table[i]:
                        table[i] = sub_result+1
        
        print(table)
        if table[amount]>amount:
            return -1
        else:
            return table[amount]

class Solution:
    def bfs(self, cur, coins):
        if cur == 0:
            return 0
        queue = [(0, cur)]
        visited = {}
        while queue:
            times, cur = heapq.heappop(queue)
            for c in coins:
                if c == cur:
                    return times +1
                if c < cur:
                    if cur-c not in visited:
                        heapq.heappush(queue, (times+1, cur- c))
                        visited[cur-c] = True
        return -1
        
    def coinChange(self, coins: List[int], amount: int) -> int:
        return self.bfs(amount, coins)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [float('inf')] * (amount+1)
        dp[0] = 0
        
        for i in range(1, amount+1):
            tmp = float('inf')
            for c in coins:
                if i - c >= 0:
                    if tmp >  dp[i-c] + 1:
                        tmp = dp[i-c] + 1
            dp[i] = tmp
        
        return dp[amount] if dp[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[amount, 0]]
        visited = set()
        for q in queue:
            for c in coins:
                if q[0] == c:
                    return q[1] + 1
                elif q[0] > c and q[0] - c not in visited:
                    visited.add(q[0] - c)
                    queue.append([q[0]-c, q[1] + 1])
        return -1
                 
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp_mat = [-1] * (amount + 1)
        dp_mat[0] = 0

        for i in range(1, amount + 1):
            min_num = None
            
            for denom in coins:
                if denom <= i and dp_mat[i - denom] != -1:
                    temp = 1 + dp_mat[i - denom]
                    if not min_num:
                        min_num = temp
                    elif temp < min_num:
                        min_num = temp
            
            if min_num:
                dp_mat[i] = min_num
        
        return dp_mat[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        dp = [float('inf')] * (amount + 1)
        calculated = [False] * (amount + 1)

        dp[0] = 0

        for coin in coins:
            if coin <= amount:
                dp[coin] = 1
                calculated[coin] = True

        for i in range(1, amount+1):
            if not calculated[i]:
                candidates = [dp[i-coin] for coin in coins if i >= coin]
                if candidates:
                    dp[i] = min(candidates) + 1
                    calculated[i] = True
                
        if dp[-1] == float('inf'):
            return -1
        else:
            return dp[-1]


class Solution:

    
    def do_du_tab(self, coins, amount):
        dp_tab = [math.inf for _ in range(amount+1)]
        dp_tab[0] = 0
        for a in range(1, amount+1):
            potential = []
            
            for coin in coins: 
                potential.append(dp_tab[a-coin]+1)
            
            dp_tab[a] = min(potential)
            
        return dp_tab[-1] if dp_tab[-1]!=float('inf') else -1

    def do_something(self, coins, amount):
        dp_tab = [math.inf for _ in range(amount+1)]
        dp_tab[0] = 0
        
        for i in range(1,amount+1):
            temp = []
            for coin in coins:
                temp.append(dp_tab[i - coin])
    
            dp_tab[i] = min(temp) + 1
        return dp_tab[amount] if dp_tab[amount]!=float('inf') else -1 
        
    def coinChange(self, coins: List[int], amount: int) -> int:
       
        if len(coins) == 0:
            return 0
        if amount == 0:
            return 0
  
        c = [coin for coin in coins if coin <= amount]
        if len(c) == 0:
           return -1
        #c.sort(reverse=True)
        return self.do_something(c, amount)
        #return self.do_du_tab(c, amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount + 1):
            curr_min = amount + 1
            for coin in coins:
                cand = amount + 1
                if i - coin >= 0:
                    cand = dp[i - coin] + 1
                if cand < curr_min: 
                    curr_min = cand
            dp[i] = curr_min
            
        return -1 if dp[len(dp) - 1] == amount + 1 else dp[len(dp) - 1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
      
      dp = [0] + [amount + 1] * amount
      
      for i in range(1, amount + 1):
        for c in coins:
          if i - c >= 0:
            if dp[i-c] < dp[i]:
              dp[i] = dp[i-c] + 1
      return dp[amount] if dp[amount] < amount + 1 else - 1
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        return self.coinChangeBFS(coins, amount)
    def coinChangeBFS(self, coins: List[int], amount: int) -> int:
        if amount == 0 or not coins:
            return 0
        queue = deque([amount])
        visited = set([amount])
        depth = 0
        while queue:
            length = len(queue)
            depth += 1
            for i in range(length):
                remaining = queue.popleft()
                for c in coins:
                    if remaining - c == 0:
                        return depth
                    elif remaining - c < 0:
                        continue
                    elif (remaining - c) not in visited:
                        queue.append(remaining- c)
                        visited.add(remaining - c)
        return -1


class Solution:
#     def do_bf(self, coins, amount, index):
#         if amount == 0:
#             return 0
#         n = len(coins)
#         if n <= index:
#             return math.inf

#         count_keeping_element = math.inf
#         if coins[index] <= amount:
#             temp = self.do_bf(coins, amount - coins[index], index)
#             if temp != math.inf:
#                 count_keeping_element = temp + 1

#         count_skipping_element = self.do_bf(coins, amount, index + 1)
#         return min(count_keeping_element, count_skipping_element)

#     def do_td_mem(self, cache, coins, amount, index):

#         if amount == 0:
#             return 0

#         if len(coins) <= index:
#             return math.inf

#         if cache[index][amount] == math.inf:
#             count_keeping_element = math.inf
#             if coins[index] <= amount:
#                 temp = self.do_td_mem(cache, coins, amount - coins[index], index)
#                 if temp != math.inf:
#                     count_keeping_element = temp + 1

#             count_skipping_element = self.do_td_mem(cache, coins, amount, index + 1)
#             cache[index][amount] = min(count_keeping_element, count_skipping_element)
#         return cache[index][amount]


    
    def do_du_tab(self, coins, amount):
        dp_tab = [math.inf for _ in range(amount+1)]
        dp_tab[0] = 0
        for a in range(1, amount+1):
            potential = []
            
            for coin in coins: 
                potential.append(dp_tab[a-coin]+1)
            
            dp_tab[a] = min(potential)
            
        return dp_tab[-1] if dp_tab[-1]!=float('inf') else -1

    
    def coinChange(self, coins: List[int], amount: int) -> int:
       
        if len(coins) == 0:
            return 0
        if amount == 0:
            return 0
  
        c = [coin for coin in coins if coin <= amount]
        if len(c) == 0:
           return -1
        #c.sort(reverse=True)

        return self.do_du_tab(c, amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        values = [-1 for i in range(amount+1)]
        values[0] = 0
        
        # loop through all coins
        for i in coins:
            # loop through values for coins to add up to
            for j in range(1,len(values)):
                # check if current coin is greater than value
                if j>=i:
                    # continue if no possible combinations
                    if values[j-i]==-1: continue
                    curr_num_coins = 1 + values[j-i]
                    # check if number of coins for value is already fewer
                    if values[j]==-1 or values[j]>curr_num_coins:
                        # update current minimum coins required
                        values[j] = curr_num_coins
        return values[amount]

class Solution:
    def coinChange(self, nums: List[int], amount: int) -> int:
        MAX = float('inf')
        dp = [0] + [MAX] * amount

        for i in range(1, amount + 1):
            dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in nums]) + 1

        return [dp[amount], -1][dp[amount] == MAX]


from collections import deque

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        dp = [float('inf')] * (amount + 1)
        dp[amount] = 0
        queue = deque()
        queue.append(amount)
        
        while len(queue) != 0:
            val = queue.popleft()
            for coin in coins:
                if val - coin == 0:
                    return dp[val] + 1
                if val - coin > 0:
                    if dp[val] + 1 < dp[val-coin]:
                        dp[val-coin] = dp[val] + 1
                        queue.append(val-coin)
            
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        MAX = amount + 1
        coins.sort(reverse=True)
        dp = [MAX]*(MAX)
        dp[0] = 0
        for i in range(1, MAX):
            dp[i] = min([dp[i-c] if i>=c else (MAX) for c in coins]) ### List Comprehension is faster
            dp[i] = dp[i] + 1 if dp[i] != MAX else dp[i]
        return -1 if (dp[-1] == MAX) else dp[-1]
    
#         coins.sort()
#         return self.countCoins(coins,amount,len(coins),0)
        
#     def countCoins(self,coins,amount,idx,count):
#         if idx < 0:
#             return -1
#         elif amount - 1 or count < 0:
#             return float('inf')
#         c = amount//coins[idx]
#         rem = amount % coins[idx]
#         if rem == 0:
#             return count+c
#         else:
#             return self.countCoins(coins,rem,idx-1,count+c)
   
       


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        MAX = float('inf')
        dp = [0] + [MAX] * amount

        for i in range(1, amount + 1):
            dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1

        return [dp[amount], -1][dp[amount] == MAX]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
#         方法一: dp[i] = min(dp[i],dp[amount-i]+1)
        # dp = [0]
        # length = len(coins)
        # for i in range(1, amount+1):
        #     dp += [9999]
        #     for j in range(length):
        #         if i >= coins[j] and dp[int(i-coins[j])] != 9999:
        #             dp[i] = min(dp[i], dp[int(i-coins[j])] + 1)
        # if dp[amount] == 9999:
        #     return -1
        # return dp[amount]

#         优化后的方法: dp[x+c] = min(dp[x]+1, dp[x+c])
        dp = [0] + [-1]*amount
        for x in range(amount+1):
            if dp[x] < 0:
                continue
            for c in coins:
                if x + c > amount:
                    continue
                if dp[x+c] < 0 or dp[x+c] > dp[x] + 1:
                    dp[x+c] = dp[x] + 1
        return dp[amount]
    
        # dp = [0] + [-1] * amount
        # for x in range(amount):
        #     if dp[x] < 0:
        #         continue
        #     for c in coins:
        #         if x + c > amount:
        #             continue
        #         if dp[x + c] < 0 or dp[x + c] > dp[x] + 1:
        #             dp[x + c] = dp[x] + 1
        # return dp[amount]


class Solution:
    def coinChangeCopy(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
    
        dp = [amount + 1] * (amount + 1)
        for i in range(amount + 1):
            if i in coins:
                dp[i] = 1
                continue
            candidates = [dp[i - coin] + 1 for coin in coins if i - coin > 0]
            if candidates:
                dp[i] = min(candidates)
        print(dp)
        return -1 if dp[amount] > amount else dp[amount]

    
    def coinChange(self, coins, amount):
        # num of coins needed for amount from 0 to \"amount\"
        if not amount:
            return 0
        
        coin_nums = [amount + 1] * (amount + 1) 
        coins_set = set(coins)
        
        for i in range(amount + 1):
            if i in coins_set:
                coin_nums[i] = 1
                continue
            candidates = [coin_nums[i - coin] + 1  for coin in coins if i - coin > 0]
            if candidates:
                coin_nums[i] = min(candidates)
        return coin_nums[amount] if coin_nums[amount] <= amount else -1
        
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        coins.sort(); cnt = 0; toCheck = {amount}
        while toCheck:
            cnt += 1; tmp = set()
            for x in toCheck:
                for y in coins:
                    if y == x: return cnt
                    if y > x: break
                    tmp.add(x-y)
            if not tmp: return -1
            toCheck = tmp
            
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for i in range(1, amount+1):
            dp[i] = min(dp[i-c] if i-c>=0 else float('inf') for c in coins) + 1
        return dp[-1] if dp[-1] != float('inf') else -1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #coins.sort()
        dp = [0] + [-1] * amount
        #print(dp)
        for coin in coins:
            for idx in range(coin, len(dp)):    
                if dp[idx - coin] != -1:
                    if dp[idx] != -1:
                        dp[idx] = min(dp[idx - coin] + 1, dp[idx])
                    else:
                        dp[idx] = dp[idx - coin] + 1
        #print(dp)
        return dp[amount]


class Solution(object):
    def coinChange(self, coins, amount):
        MAX = float('inf')
        dp = [0] + [MAX] * amount

        for i in range(1, amount + 1):
            dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1
        if dp[amount] == MAX:
            return -1
        return dp[amount]
        return [dp[amount], -1][dp[amount] == MAX]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0]+[-1]*amount
        for i in range(len(dp)):
            if dp[i] < 0:
                continue
            for x in coins:
                if i+x>amount:
                    continue
                if dp[i+x] == -1 or dp[i+x] >dp[i]+1:
                    dp[i+x] = dp[i]+1
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount+1)
        dp[0] = 0
        
        for i in range(min(coins), amount+1):
            tmp = list()
            for coin in coins:
                if (i-coin) < 0:
                    tmp.append(float('inf'))
                else:
                    tmp.append(dp[i-coin])
            dp[i] = min(tmp) + 1
        
        return dp[-1] if dp[-1]!= float('inf') else -1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount: return 0
        coins = [coin for coin in coins if coin <= amount]
        if not coins: return -1
        dp = [0 for i in range(amount+max(coins)+10)]
        for coin in coins:
            dp[coin] = 1
        for i in range(1, amount + 1):
            for coin in coins:
                if dp[i] >= 1:
                    if dp[i+coin] == 0: 
                        dp[i+coin] = dp[i] + 1
                    else:
                        if dp[i] + 1 < dp[i+coin]:
                            dp[i+coin] = dp[i] + 1
        if not dp[amount]: return -1
        return dp[amount]

class Solution(object):
    def coinChange(self, coins, amount):
        MAX = float('inf')
        dp = [0] + [MAX] * amount

        for i in range(1, amount + 1):
            dp[i] = min([dp[i - c] if i - c >= 0 else MAX for c in coins]) + 1

        return [dp[amount], -1][dp[amount] == MAX]


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):
                if coin <= x:
                    dp[x] = min(dp[x], dp[x-coin]+1)
                
        return dp[amount] if dp[amount] != float('inf') else -1
        '''
        '''
        def helper(amount):
            if amount < 0:
                return -1
            elif amount == 0:
                return 0
            minCoins = None
            for coin in coins:
                totCoins = 1 + helper(amount - coin)
                
                if totCoins > 0 and (minCoins is None or totCoins < minCoins):
                    minCoins = totCoins
            if minCoins is None:
                return -1
            else:
                return minCoins
        return helper(amount)
        '''
        def dfs(start, amt, n_coins):
            nonlocal min_coins

            coin = coins[start]

            # LHS = lower bound on number of coins, achieved using the current coin
            # Return early since we can't possibly achieve original \"amount\"
            # along this path.
            # For this particular solution, this check isn't necessarily,
            # since there is another check within the loop below. However, it
            # speeds up the solution. Better to have this check before the
            # \"amt == 0\" check below.
            #if n_coins + (amt + coin - 1) / coin > min_coins:
            #if (min_coins - n_coins - 1) * coin + 1 < amt:
            #    return

            div = amt // coin
            n_coins += div
            amt %= coin
            
            if amt == 0:
                min_coins = min(min_coins, n_coins)
                return
            
            if start < len_coins:
                # use as many of current coin as possible, and try next smaller coin
                dfs(start + 1, amt, n_coins)

                # Always greedily taking as many of biggest coins as possible doesn't work.
                # \"Backtrack\" by using 1 less of current coin per iteration, and
                # trying the next smaller coin.

                next_coin = coins[start + 1]
                
                for _ in range(div):
                    amt += coin 
                    n_coins -= 1
                    
                    if (min_coins - n_coins - 1) * next_coin + 1 > amt:
                    #if (min_coins - n_coins) * next_coin > amt: # hope still exists
                        dfs(start + 1, amt, n_coins)
                    else:
                        break
        
        len_coins = len(coins) - 1
        
        # try biggest coins first
        coins.sort(reverse=True)
        
        min_coins = float('inf')

        dfs(0, amount, 0)
        
        return min_coins if min_coins < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        a = [amount+1]*(amount+1)
        a[0] = 0
        
        for i in range(1,amount+1):
            mlist = [a[i-c] for c in coins if i-c>=0]
            if len(mlist)>0:
                a[i] = 1+min(mlist)

        print(a)
        if a[amount] > amount:
            return -1
        else:
            return a[amount]
            
                
                    
                
            
            
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        coins_set = set(coins)
        for i in range(amount + 1):
            if i in coins_set:
                dp[i] = 1
                continue
            candidates = [dp[i - c] + 1 for c in coins if i - c >= 0]
            if candidates:
                dp[i] = min(candidates)
        return dp[amount] if dp[amount] <= amount else -1

import math

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        path_length = []
        path_length.append(0)
        for value in range(1, amount + 1):
            min_path_value = math.inf
            for coin in coins:
                path_index = value - coin
                
                if path_index > -1 and path_length[path_index] != -1:
                    current_min_path = path_length[path_index] + 1
                    if current_min_path < min_path_value:
                        min_path_value = current_min_path
                        
            path_length.append(min_path_value)
        
        return path_length[amount] if path_length[amount] < math.inf else -1 
    
    
        
   


    
            
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        cache = [0] * (amount+1)
        for i in range(1, amount+1):
            min_count = float('inf')
            
            for coin in coins:
                if i-coin >= 0:
                    current_min_coin = cache[i-coin] + 1
                    if min_count > current_min_coin:
                        min_count = current_min_coin
                        
            cache[i] = min_count
        
        if cache[amount] in [0, float('inf')]:
            return -1
            
        return cache[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        Max = float('inf')
        
        dp = [0] + [Max] * amount
        
        for i in range(1, amount + 1):
            dp[i] = min(dp[i - c] if i - c >= 0 else Max for c in coins) + 1
        return dp[amount] if dp[amount] != Max else - 1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [None for i in range(amount + 1)]
        
        dp[0] = 0
        
        for i in range(1, amount + 1):
            result = None
            for coin in coins:
                if i - coin >= 0:
                    tmp = dp[i - coin]
                    if tmp is not None and (result is None or tmp + 1 < result):
                        result = tmp + 1
            dp[i] = result
        
        if dp[-1] is None:
            return -1
        
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        #let arr[i] be the min amt of coins needed to produce amount i
        arr = [float('inf')] * (amount+1)
        arr[0] = 0 
        
        for i in range(1, len(arr)): 
            min_choices = []
            for x in coins: 
                if i - x >= 0: 
                    min_choices.append(1 + arr[i-x])
            
            if min_choices: 
                arr[i] = min(min_choices)
        
        if arr[amount] == float('inf'): 
            return -1
        else: 
            return arr[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if not coins and amount:
            return -1
        
        if not amount:
            return 0
        
        target = [0]*(amount+1)
        target[0] = 1
        
        #coins[::-1].sort()
        
        for i in range(len(coins)-1, -1, -1):
            
            coin = coins[i]
            j = 0
            while j <= amount:
                
                if target[j] == 0:
                    j+=1
                    continue
                
                next_idx = j+coin
                if next_idx > amount:
                    break
                
                if target[j] > 0 and (target[next_idx] > target[j]) or target[next_idx] == 0:
                    target[next_idx] = target[j] + 1
                
                j+=1
        
        
        print(target)
        if target[amount]:
            return target[amount]-1
        else:
            return -1

class Solution:
    
    def __init__(self):
        self.min_coins = float('inf')
        
    def coinChange(self, coins: List[int], amount: int) -> int:    
        def getChange(num_coins, need, start):
            divided_coin = need // coins[start]
            
            if num_coins + divided_coin >= self.min_coins:
                return
            
            if need % coins[start] == 0:
                self.min_coins = min(self.min_coins, divided_coin + num_coins)
                return
            
            if start == len(coins) - 1:
                return
            
            for num_used in range(divided_coin, -1, -1):
                new_need = need - (coins[start] * num_used)    
                getChange(num_coins + num_used, new_need, start + 1)

        coins = sorted(coins, reverse=True)
        getChange(0, amount, 0)
        
        return self.min_coins if self.min_coins < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        
        min_coins = [-1 for x in range(amount + 1)]
        
        min_coins[0] = 0
        
        for value in range(1, amount+1):
            for coin in coins:
                if coin <= value:
                    if min_coins[value-coin] != -1:
                        
                        if min_coins[value] == -1:
                            min_coins[value] = min_coins[value-coin] + 1
                        elif min_coins[value-coin] + 1 < min_coins[value]:
                        
                        
                            min_coins[value] = min_coins[value-coin] + 1
        
        return min_coins[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #In any dp probelm we need to identify the recursive relationship. 
        #The solution to a problem can be found with teh help of its sub problems. 
        '''
        In this case we define teh recursive relationship as follows:
        Where Opt(S) is the minimum number of coins in [c1, c2, .., cn] that are needed to create amount S. 
        
        The following recursive relationship holds:
        Opt(S) = Opt(S - C) + 1, where C is the denomination of [c0, c1, ..., cn-1] and S is the amount.   
        
        Minimum number of coins for amoun S in denomination cn, is given by the minimum amount of (amount - previouse denomination) for all coins.  
        
        But we dont know the denomination of the last coin C, so we compute 
        Opt(S - ci) for all possible denominations c0, c1, ..., cn, and choose the minimum among them. 
        
        Subject to S - ci >= 0. 
        
        Base cases:
        Opt(S) = 0, when S = 0. S is the amount.
        Opt(S) = -1, when n = 0. 
        
        * Each sub porblem (coin) has S computations. 
        * And the number of subproblems is O(n * S). 
        
        '''
        #What will we identify subproblems by. (S - ci)
        #or by an amount. So as we are calculating the minimum number of coind needed for each amount, we need to look at the minimum number of coins need using c0, c1, .., cn. 
        #Sub problems are determined by amounts, so we cache them by amount. 
        #Fir every sub problem need optimal number of coins for each denomination. Then we take the minimum of all teh minimums, and that is the minimum number of coins needed to represnet an amount. 
        
        
        T = [float('inf')] * (amount + 1)
        T[0] = 0

        #In an iteratoive solution we move left to right and have to compute teh minimum number of coins for all amounts before this one. 
        
        for amnt in range(1, amount + 1):
            curr_min = float('inf')
            for k in range(len(coins)):
                #calculate the minimum number of coins needed for each denotion to represnet the amount given by amnt. 
                #then take minimum and assign it to T[amnt].
                if coins[k] <= amnt:
                    val = 1 + T[amnt - coins[k]]
                    if val < curr_min:
                        curr_min = val
            
            T[amnt] = curr_min

        return -1 if T[-1] == float('inf') else T[-1]
                
        
        
        
            
        
        
        


from typing import List

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
        solution = -1
        dp = [0] * (amount + 1)
        for i in range(1, amount + 1):
            dp[i] = min([dp[i - coin] for coin in coins if i - coin >= 0] + [10000]) + 1

        if dp[-1] != 10001:
            solution = dp[-1]
        return solution

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        coins.sort()
        for i in range(1, amount+1):
            tmp = [float('inf')]
            for coin in coins:
                if i-coin < 0: 
                        break 
                tmp.append(dp[i-coin])
            dp[i] = min(tmp) + 1
            
        return dp[amount] if dp[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins, reverse=True)
        dp = [-1] * (amount + 1)
        dp[0] = 0
        for i in range(1, amount + 1):
            if i - coins[-1] < 0:
                continue
            for coin in coins:
                index = i - coin
                if index >= 0 and dp[index] != -1:
                    value = dp[index] + 1
                    if dp[i] == -1 or dp[i] > value:
                        dp[i] = value
        return dp[amount]
                
        


# class Solution:
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         dp=[float('inf')]*(amount+1)
#         dp[0]=0
        
#         for i in range(1, amount+1):
#             for c in coins:
#                 if c<=i:        #check if coin value fits in current amount i
#                     dp[i]=min(dp[i], dp[i-c]+1)
        
#         if dp[amount]>amount:
#             return -1
#         else:
#             return dp[amount]
                    
    
    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[float('inf')]*(amount+1)
        dp[0]=0
        
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i]=min(dp[i], dp[i-coin]+1)
                
        return dp[amount] if dp[amount] !=float('inf') else -1

import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [math.inf] * amount
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        return dp[-1] if dp[-1] != math.inf else -1
        
#         dp = [0] + [math.inf] * amount
#         for coin in coins:
#             for i in range(coin, amount + 1):
#                 dp[i] = min(dp[i], dp[i - coin] + 1)
#         return dp[-1] if dp[-1] != math.inf else -1
        



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 
        
        
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        numbers = [float('inf')]*(amount+1)
        numbers[0] = 0
        
        for coin in coins:
            for x in range(coin, amount+1):
                numbers[x] = min(numbers[x], numbers[x-coin]+1)
        
        return numbers[amount] if numbers[amount] < float('inf') else -1
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        self.ret = float('inf')


        
        def recurrent(index, coins, amount, current):    
            first = coins[index]
            n = amount // first
            remainder = amount % first
            if remainder == 0:
                self.ret = min(self.ret, current + n)
            else:
                l = len(coins)
                while remainder <= amount and index<l-1 and current + n < self.ret:
                    recurrent(index+1, coins, remainder, current + n)
                    n -= 1
                    remainder += first

            return
        
        
        
        recurrent(0, coins, amount, 0)
        
        
        
        if self.ret == float('inf'):
            return -1
        else:
            return self.ret
        



class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')]*(amount+1)
        dp[0] = 0
        
        for coin in coins:
            for i in range(coin, amount + 1):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        return dp[amount] if dp[amount] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount <= 0:
            return 0
        
        f = [float('inf')] * (amount + 1)
        f[0] = 0
        for c in coins:
            for a in range(c, amount + 1):
                f[a] = min(f[a], f[a - c] + 1)
        return f[amount] if f[amount] != float('inf') else -1

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 

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        
        return dp[-1] if dp[-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        numWays = [float('inf')] * (amount + 1)
        
        numWays[0] = 0
        
        for coin in coins:
            for value in range(coin, amount + 1):
                numWays[value] = min(numWays[value], 1 + numWays[value - coin])
                
        print(numWays[amount])
        return numWays[amount] if numWays[amount] != float('inf') else -1

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

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [float('inf')] * (amount + 1)
        
        dp[0] = 0
        
        for coin in coins:
            for j in range(coin, amount+1):
                dp[j] = min(dp[j], dp[j - coin] + 1)
                
        if dp[amount] != float('inf'):
            return dp[amount]
        else:
            return -1
            
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #coins = sorted(coins)
                
        dp = [amount+1 for _ in range(amount+1)]
        dp[0] = 0
        
        for coin in coins:
            for a in range(coin, amount+1):
                dp[a] = min(dp[a], dp[a-coin]+1)
                            
        res = dp[amount] if dp[amount] < amount+1 else -1
            
        return res


class Solution:
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        
        mem = [-1] * (amount+1)
        mem[0] = 0
        
        for i in range(1, amount+1):
            m = math.inf
            for c in coins:
                if i-c >= 0:
                    if mem[i-c] != -1 and mem[i-c] + 1 < m:
                        m = mem[i-c] + 1
            
            if m != math.inf:
                mem[i] = m
        print(mem)
        return mem[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        
        return dp[-1] if dp[-1] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount+1)
        #dp[i] = min (dp[i - c1], dp[i - c2], ...) + 1
        dp[0] = 0

        for coin in coins:
            for i in range(coin, len(dp)):
                dp[i] = min(dp[i], dp[i - coin] + 1)
                
        return dp[amount] if dp[amount] != float('inf') else -1

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 


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

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        numWays = [float('inf')] * (amount + 1)
        
        numWays[0] = 0
        
        for coin in coins:
            for value in range(coin, amount + 1):
                numWays[value] = min(numWays[value], 1 + numWays[value - coin])
                
                
        return numWays[amount] if numWays[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first i coins with sum of j
        # dp[i][j] = min(dp[i-1][j], dp[i][j-k]+1) for k in coins
        dp = [0] + [float('inf')] * amount
        for c in coins:
            for j in range(c, amount+1):
                dp[j] = min(dp[j], dp[j-c]+1)
        return dp[-1] if dp[-1] < float('inf') else -1


        

'''    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for i in range(n):
                if j >= coins[i]:
                    dp[j] = min(dp[j], dp[j-coins[i]]+1)
        return dp[-1] if dp[-1] < float('inf') else -1
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        @lru_cache(None)
        def dp(remain, cnt):
            if remain == 0:return cnt 
            ans = float('inf')
            for c in coins:
                if c*(ans-cnt)>remain>=c: 
                    ans = min(ans, dp(remain-c, cnt+1))
            return ans 
        ans = dp(amount, 0) 
        return ans if ans < float('inf')  else -1
    
    
    def coinChange(self, coins, amount):
        coins.sort(reverse = True)
        lenc, self.res = len(coins), 2**31-1

        def dfs(pt, rem, count):
            if not rem:
                self.res = min(self.res, count)
            for i in range(pt, lenc):
                if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                    dfs(i, rem-coins[i], count+1)

        for i in range(lenc):
            dfs(i, amount, 0)
        return self.res if self.res < 2**31-1 else -1
'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [math.inf] * (amount + 1)
        dp[0] = 0
        
        for coin in coins:
            for idx in range(coin, amount+1):
                # Get the coin count without this coin
                prev_coins_ct = dp[idx - coin]
                # Get the minimum of either adding from prev coin ct or current lowest
                dp[idx] = min(prev_coins_ct + 1, dp[idx])
        
        if dp[-1] == math.inf:
            return -1
        
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [-1]*(amount+1)
        dp[0] = 0
        
        for idx in range(amount+1):
            tmp = []
            for coin in coins:
                if idx-coin >= 0 and dp[idx-coin] != -1:
                    tmp.append(dp[idx-coin])
            if tmp:
                dp[idx] = min(tmp) + 1
        print(dp)
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [ 0 ] + [ float( 'inf' ) ] * ( amount )
        for c in coins :
            for j in range( c , amount + 1 ) :
                dp[ j ] = min( dp[ j - c ] + 1 , dp[ j ] )
        if dp[ -1 ] != float( 'inf' ) : return dp[ -1 ]
        return -1
    #Sanyam Rajpal


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        numWays = [float('inf')] * (amount + 1)
        
        numWays[0] = 0
        
        for coin in coins:
            for value in range(coin, amount + 1):
                numWays[value] = min(numWays[value], 1 + numWays[value - coin])
                        
        return numWays[amount] if numWays[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        print(dp)
        
        for coin in coins:
            for i in range(coin, amount+1):
                dp[i] = min(dp[i], dp[i-coin]+1)
        
        return dp[-1] if dp[-1] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount<0:
            return -1
        coins=sorted(coins)
        d=[amount+1]*(amount+1)
        d[0]=0
        for i in range(amount+1):
            for j in coins:
                if j<=i:
                    d[i]=min(d[i],d[i-j]+1)
                else:
                    break
        if d[-1]>amount:
            return -1
        else:
            return d[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(len(dp)):
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[-1] if dp[-1] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        if len(coins) == 1 and coins[0] > amount:
            return -1
        
        dp = [amount+1] * (amount+1)
        dp[0] = 0
        for i in range(1,amount+1):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i-coin] + 1, dp[i])
        return dp[amount] if dp[amount] != amount+1 else -1
    
#         rs = [amount+1] * (amount+1)
#         rs[0] = 0
#         for i in xrange(1, amount+1):
#             for c in coins:
#                 if i >= c:
#                     rs[i] = min(rs[i], rs[i-c] + 1)

#         if rs[amount] == amount+1:
#             return -1
#         return rs[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        dp = [-1] * (amount + 1)
        dp[0] = 0
        
        for i in range(len(coins)):
            if coins[i] <= amount:
                dp[coins[i]] = 1

        for i in range(1, amount+1):
            if dp[i] == -1:
                comparison = []
                for c in coins:
                    if i - c >= 0 and dp[i-c] != -1:
                        comparison.append(dp[i-c])
                if comparison:
                    dp[i] = min(comparison) + 1
        
        return dp[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = [float('inf') for _ in range(amount+1)]
        memo[0] = 0
        for c in coins:
            for i in range(c, len(memo)):
                memo[i] = min(1 + memo[i-c], memo[i])
        
        return memo[amount] if memo[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1] * (amount+1)
        dp[0] = 0
        for i in range(1, amount+1):
            for j in coins:
                if i >= j:
                    dp[i] = min(dp[i], dp[i-j] + 1)

        if dp[amount] == amount+1:
            return -1
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        rs = [amount+1] * (amount+1)
        rs[0] = 0
        for i in range(1, amount+1):
            for c in coins:
                if i >= c:
                    rs[i] = min(rs[i], rs[i-c] + 1)

        if rs[amount] == amount+1:
            return -1
        return rs[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(len(dp)):
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[i] if dp[i] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(len(dp)):
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[amount] if dp[amount] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
#        1 2 3 4 5 6 7 8 9 10 11
#      0 
# 1    0 1 1 2 2 1 2 2 3 4 2 3
# 2    0   1 2     
# 5    0  
        dp = [float('inf')] * (amount+1)
        dp[0] = 0 

        for i in range(1,amount+1):
            for c in coins:
                if c<=i:
                    dp[i] = min(dp[i], dp[i-c] + 1)
        
        return dp[-1] if dp[-1] != float('inf') else -1
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        # sort list descedning order
    
#     coins = [1,2,5] 1 + 4 = 5 -> coinchange(4) + 1
#                     2 + 3 = 5 -> coinchange(3) + 1
#                     5 + 0 = 5 -> coinchange(0) + 1
           
#     amount                 0  1  2  3  4. 5..  10 11
        
#     fewest num of coins    0  1  1  2 2  min()
            
#         coin in coins
#             if amount != coin:
#                 dp[amount] = dp[amount-coin] + 1
                                
        
#         def dfs(amount):
            
#             # Have we solved this before
#             if amount in memo:
#                 return memo[amount]
            
#             # recursive base case
#             if amount == 0:
#                 return 0
            
#             # find all cases
#             tmp = []
#             for i in range(len(coins)):
                
#                 if amount - coins[i] >= 0:
#                     tmp.append(dfs(amount-coins[i],))
                    
#                 else:
#                     tmp.append(float('inf'))
                
            
#             # save the output in memory
#             _min = min(tmp) + 1
#             memo[amount] = _min
            
#             return _min
        
# #         memo = {}
# #         output = dfs(amount)
        
# #         return output if output != float('inf') else -1
#         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
        
    
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp=[sys.maxsize]*(amount+1)
        
        dp[0]=0
        for i in range(1,len(dp)):
            for c in coins:
                if c<=i:
                    dp[i]=min(dp[i],dp[i-c]+1)
                    
        if dp[amount] == sys.maxsize:
            return -1
        return dp[amount]
            
        
        


import math


def rec(coins, amount, used_coins, res):
    if amount == 0:
        res[0] = min(res[0], used_coins)        
    elif coins and (res[0] - used_coins) * coins[-1] >= amount:
        for i in range(amount // coins[-1], -1, -1):
            rec(coins[:-1], amount - i * coins[-1], used_coins + i, res)

                
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        res = [math.inf]
        rec(sorted(coins), amount, 0, res)
        return res[0] if res[0] != math.inf else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        for i in range(1, amount + 1):
            for c in coins:
                if i >= c:
                    dp[i] = min(dp[i], 1 + dp[i-c])
        
        return dp[-1] if dp[-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or len(coins) == 0:
            return 0
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        for i in range(amount + 1):
            for j in coins:
                if j <= i:
                    dp[i] = min(dp[i], dp[i - j] + 1)
        if dp[-1] > amount:
            return -1
        else:
            return dp[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        for coin in coins:
            for i in range(amount + 1):
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[-1] if dp[-1] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        rs = [amount+1] * (amount+1)
        rs[0] = 0
        for i in range(1, amount+1):
            for c in coins:
                if i >= c:
                    rs[i] = min(rs[i], rs[i-c] + 1)

        if rs[amount] == amount+1:
            return -1
        return rs[amount]
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        if amount == 0:
            return 0
        changes = [amount+1] *(amount+1)
        changes[0] = 0
        for i in range(1, amount+1):
            for coin in coins:
                if coin > i:
                    break
                else:
                    changes[i] = min(changes[i], changes[i-coin] + 1)
        if changes[-1]!=(amount+1): 
            return changes[-1] 
        else:
            return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return amount
        
        dp = [1e9 for _ in range(amount+1)]
        dp[0] = 0
        
        for i in range(1, amount + 1):
            k = 1e9         # K = min num of coins to reach amount i
            for c in coins:
                if i - c >= 0:
                    k = min(k, dp[i-c] + 1)
            if k != 1e9:
                dp[i] = k
        
        return -1 if dp[amount] == 1e9 else dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [1e9 for _ in range(amount+1)]
        dp[0] = 0
        
        for i in range(1, amount + 1):
            k = 1e9         # K = min num of coins to reach amount i
            for c in coins:
                if i - c >= 0:
                    k = min(k, dp[i-c] + 1)
            if k != 1e9:
                dp[i] = k
        
        return -1 if dp[amount] == 1e9 else dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
                
        dp = [amount+1 for _ in range(amount+1)]
        dp[0] = 0
        
        for coin in coins:
            for a in range(coin, amount+1):
                if coin > a:
                    break
                dp[a] = min(dp[a], dp[a-coin]+1)
                            
        res = dp[amount] if dp[amount] < amount+1 else -1
            
        return res


class Solution(object):
    def coinChange(self, coins, amount):
        dp = [0] + [float('inf')] * amount
        
        for i in range(1, amount + 1):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        
        return dp[-1] if dp[-1] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for i in range(amount+1)]
        dp[0] = 0
        
        for coin in coins:
            for j in range(coin, amount+1):
                dp[j] = min(dp[j], 1+dp[j-coin])
        
        return dp[-1] if dp[-1]!=float('inf') else -1

'''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for i in range(n):
                if j >= coins[i]:
                    dp[j] = min(dp[j], dp[j-coins[i]]+1)
        return dp[-1] if dp[-1] < float('inf') else -1
'''    
class Solution:
    '''
    def coinChange(self, coins: List[int], amount: int) -> int:
        @lru_cache(None)
        def dp(remain, cnt):
            if remain == 0:return cnt 
            ans = float('inf')
            for c in coins:
                if remain>=c: 
                    ans = min(ans, dp(remain-c, cnt+1))
            return ans 
        ans = dp(amount, 0) 
        return ans if ans < float('inf')  else -1
    '''          
    
    
    def coinChange(self, coins, amount):
        coins.sort(reverse = True)
        lenc, self.res = len(coins), 2**31-1

        def dfs(pt, rem, count):
            if not rem:
                self.res = min(self.res, count)
            for i in range(pt, lenc):
                if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                    dfs(i, rem-coins[i], count+1)

        for i in range(lenc):
            dfs(i, amount, 0)
        return self.res if self.res < 2**31-1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        dp = [amount+1 for _ in range(amount+1)]
        dp[0] = 0
        
        for i in range(1, amount+1):
            for coin in coins:
                if coin > i:
                    break
                #print(i, coin, len(dp))
                #dp[i//coin] + dp[i%coin]
                dp[i] = min(dp[i], dp[i-coin]+1)
                
        #print(dp)
            
        res = dp[amount] if dp[amount] < amount+1 else -1
            
        return res

import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        count = [math.inf] * (amount + 1)
        count[0] = 0
        for i in range(1, amount + 1):
            for coin in coins:
                if i >= coin:
                    count[i] = min(count[i], count[i-coin] + 1)
        
        return count[amount] if count[amount] != math.inf else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #coins = sorted(coins)
                
        dp = [amount+1 for _ in range(amount+1)]
        dp[0] = 0
        for a in range(1, amount+1):
            for coin in coins:
                if coin <= a:
                    dp[a] = min(dp[a], dp[a-coin]+1)
                            
        res = dp[amount] if dp[amount] < amount+1 else -1
            
        return res

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        table = [amount+1]*(amount+1)
        table[0] = 0 
        
        for i in range(1, amount+1):
            for coin in coins:
                if coin <= i:
                    table[i] = min(table[i-coin]+1, table[i])
        print(table)
        return table[amount] if table[amount] != amount+1 else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        @lru_cache(maxsize=None)
        def giveChange(target):
            if target == 0:
                return 0
            if target < 0:
                return -1
            
            minForTarget = None
            for coin in coins:
                result = giveChange(target - coin)
                if result != -1:
                    minForTarget = result + 1 if not minForTarget else min(result + 1, minForTarget)
            
            return minForTarget or -1
            
        return giveChange(amount)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [0] + [amount+1 for i in range(amount)]
        for i in range(1, amount+1):
            for c in coins:
                if i >= c:
                    dp[i] = min(dp[i-c]+1, dp[i])
                    
        return dp[amount] if dp[amount] < amount+1 else -1
    
    

#     def coinChange(self, coins: List[int], amount: int) -> int:
#         n = len(coins)
#         dp = [[amount+1 for j in range(n+1)] for i in range(amount+1)]
#         for j in range(n+1):
#             dp[0][j] = 0
#         for i in range(1, amount+1):
#             for j in range(1, n+1):
#                 if coins[j-1] > i:
#                     dp[i][j] = dp[i][j-1]
#                 else:
#                     dp[i][j] = min(dp[i-coins[j-1]][j]+1, dp[i][j-1])
            
#         return dp[amount][n] if dp[amount][n] < amount+1 else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        print(dp)
        
        for coin in coins:
            for i in range(len(dp)):
                if coin <= i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        
        return dp[-1] if dp[-1] != float('inf') else -1
        
        '''
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #brute force keep looking for ways until it == 0
        dp = [float('inf')] * (amount+1)
        dp[0] = 0 #store the number of ways to 0
        
        for coin in coins: #check if dp[i] > coin then return the min
            for i in range(1,amount+1):
                if i - coin >= 0: #valid combination
                    dp[i] = min(dp[i],dp[i-coin] + 1)
        if dp[amount] < float('inf'):
            return dp[-1]
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        self.min = 2**31-1

        def dfs(index, amount, count):
            if amount==0:
                self.min = min(self.min, count)
            if amount < 0:
                return 
            for i in range(index, len(coins)):
                if coins[i] * (self.min-count)>amount >= coins[i]: # if hope still exists
                    dfs(i, amount-coins[i], count+1)
                    
        dfs(0, amount, 0)
        return self.min if self.min != 2**31-1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        ary = [amount+1] * (amount + 1)
        ary[0] = 0

        for i in range(1, len(ary)):
            for coin in coins:
                if i-coin < 0:
                    continue
                ary[i] = min(ary[i], ary[i-coin] + 1)
        
        return ary[amount] if ary[amount] < amount + 1 else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        numOfCoins = [float('inf') for x in range(amount + 1)]
        numOfCoins[0] = 0
        for coin in coins:
            for num in range(len(numOfCoins)):
                if coin <= num:
                    numOfCoins[num] = min(numOfCoins[num], numOfCoins[num - coin] + 1)
        return numOfCoins[amount] if numOfCoins[amount] != float('inf') else -1

''''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first i coins with sum of j
        # dp[i][j] = min(dp[i-1][j], dp[i][j-k]+1) for k in coins
        dp = [0] + [float('inf')] * amount
        for c in coins:
            for j in range(c, amount+1):
                dp[j] = min(dp[j], dp[j-c]+1)
        return dp[-1] if dp[-1] < float('inf') else -1


        

'''    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for c in coins:
                if j >= c:
                    dp[j] = min(dp[j], dp[j-c]+1)
        return dp[-1] if dp[-1] < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        MAX = float('inf')
        dp = [0] + [MAX]*amount

        for i in range(1, amount+1):
            for coin in coins:
                if i - coin >= 0:
                    dp[i] = min(dp[i], dp[i-coin] + 1)

        if dp[-1] == MAX:
            return -1
        return dp[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        MAX = amount+10
        dp = [MAX for i in range(amount+1)]
        dp[0] = 0
        for i in range(1, amount+1):
            curr = i
            for coin in coins:
                prev = curr-coin
                if prev >= 0:
                    dp[curr] = min(dp[curr], dp[prev] + 1)
                    
    
        return dp[amount] if dp[amount] != MAX else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # 0-1 bag, dp[i][j] is the minimum number of coins only use the first i coins can sum up to target j
        dp = [-1 for i in range(amount+1)]
        dp[0] = 0
        
        for coin in coins:
            for i in range(coin, amount+1):
                if i == coin:
                    dp[i] = 1
                elif dp[i] == -1 and dp[i-coin] != -1:
                    dp[i] = dp[i-coin] + 1
                elif dp[i] != -1 and dp[i-coin] != -1:
                    dp[i] = min(dp[i], dp[i-coin] + 1)
            
        return dp[-1] 

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def change_memo(coins, amount, memo):
            if amount not in memo:
                if amount == 0:
                    best = 0
                elif amount < 0:
                    best = -1
                else:
                    best = None
                    for c in coins:
                        res = change_memo(coins, amount-c, memo)
                        if res == -1:
                            continue
                        if (best is None) or (res < best):
                            best = res
                    if best is None:
                        best = -1
                    else:
                        best += 1
                memo[amount] = best
                
            return memo[amount]
        
        memo = {}
        change_memo(coins, amount, memo)
        return memo[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        min_coins = float('inf')

        def count_coins(start_coin, coin_count, remaining_amount):
          nonlocal min_coins

          if remaining_amount == 0:
            min_coins = min(min_coins, coin_count)
            return

          # Iterate from largest coins to smallest coins
          for i in range(start_coin, len(coins)):
            remaining_coin_allowance = min_coins - coin_count
            max_amount_possible = coins[i] * remaining_coin_allowance

            if coins[i] <= remaining_amount and remaining_amount < max_amount_possible:
              count_coins(i, coin_count + 1, remaining_amount - coins[i])

        count_coins(0, 0, amount)
        return min_coins if min_coins < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [float('inf')]*(amount+1)
        dp[0] = 0
        
        for i in range(amount+1):
            for coin in coins:
                if i-coin>=0:
                    dp[i] = min(dp[i], 1+dp[i-coin])
        
        if dp[-1] == float('inf'):
            return -1
        return dp[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # initialize a dp table for the minimum amount of coins needed
        # to get each value up to the amount (all except 0 are inf)
        dp = [0] + [float('inf')] * amount
        
        # then for i up to the amount and counting each coin
        for i in range(1, amount+1):
            for c in coins:
                # if you can subtract the coin from the current amount
                # get new min (current or with the new coin)
                if i - c >= 0:
                    # dp[i-c] + 1: minimum of the amount subtracting the coin
                    # we are at now if we added this 1 coin
                    dp[i] = min(dp[i], dp[i-c]+1)
        
        if dp[-1] == float('inf'): return -1
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # f(amount) = min(f(amount-1), f(amount-2), f(amount-5))
        cap = amount+1
        dp = [cap] * (amount+1)
        dp[0] = 0
        for i in range(1, amount+1):
            for c in coins:
                dp[i] = min(dp[i-c]+1 if i-c>=0 else cap, dp[i])
        return dp[amount] if dp[amount]< cap else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for i in range(amount + 1)]
        dp[0] = 0
        
        for i in range(1, len(dp)):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i], 1 + dp[i - coin])
        
        if dp[len(dp) - 1] == float('inf'):
            return -1
        return dp[len(dp) - 1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        curr = [float('inf')]*(amount+1)
        curr[0] = 0
        coins = sorted(coins)
        for num in range(amount+1):
            for c in coins:
                if num-c < 0: break
                curr[num] = min(curr[num], curr[num-c]+1)
        return curr[amount] if curr[amount]<float('inf') else -1
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount + 1):
            for j in coins:
                if i - j >= 0:
                    dp[i] = min(dp[i-j] + 1, dp[i])
        
        return dp[-1] if dp[-1] != float('inf') else -1
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        result = -1

        def helper(coins: List[int], pos: int, left: int, current: int):
            nonlocal result
            if left % coins[pos] == 0:
                if result == -1 or result > current + left // coins[pos]:
                    result = current + left // coins[pos]
                return

            if pos == len(coins) - 1:
                return

            for k in range(left // coins[pos], -1, -1):
                new_amount = left - coins[pos] * k
                new_count = current + k
                if result != -1 and result < new_count + (new_amount + coins[pos + 1] - 1) / coins[pos + 1]:
                    break
                helper(coins, pos + 1, left - coins[pos] * k, current + k)

        helper(coins, 0, amount, 0)
        return result

import sys
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        [1,2,3] 7 => 3
        
        7-3 = 4
        4-3 = 1
        1-3 = -2 not possible
        1-2 = -1 not
        1-1 = 0 return 
        0 1 2 3 4 5 6 7
        3 2 2 2 1 1 1 1
              &
        '''
        dp = [sys.maxsize] * (amount+1)
        if amount==0: return 0
        dp[amount] = 0
        for i in range(amount,-1,-1):
            for coin in coins:
                if i-coin >= 0:
                    dp[i-coin] = min(dp[i]+1, dp[i-coin])
        return -1 if dp[0] == sys.maxsize else dp[0]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        @lru_cache(maxsize=None)
        def _recursive(a):
            if a == 0:
                return 0
            min_n = 1e9
            for c in coins:
                if c <= a:
                    sub_a = _recursive(a - c)
                    if sub_a != -1:
                        min_n = min(min_n, sub_a + 1)
            return min_n if min_n != 1e9 else -1
        return _recursive(amount)
    
    # Button-up
    # def coinChange(self, coins: List[int], amount: int) -> int:
        # states = [0] * (amount + 1)
        # for s in range(0, amount+1):
        #     if s == 0:
        #         states[s] = 0
        #     else:
        #         min_n = 1e9
        #         for c in coins:
        #             if c <= s and states[s-c] != -1:
        #                 min_n = min(min_n, 1 + states[s-c])
        #         if min_n == 1e9:
        #             states[s] = -1
        #         else:
        #             states[s] = min_n
        # return states[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        res = {}
        res[0] = 0
        
        for x in range(amount+1)[1:]:
            prevs = [x-c for c in coins]
            
            
            
            this_res = min([res.get(y,2**31) for y in prevs])+1
            
            res[x] = this_res
        # print(res)
        if res[amount] >= 2**31:
            return -1
        else:
            return res[amount]
                    
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        coins.sort()
        coins.reverse()
        print(coins)
        change = [-1]*(amount+1)
        change[0] = 0
        for i in range(1, amount+1):
            for c1 in coins:
                #print(f'i:{i}, c1:{c1}')
                if c1 > i:
                    continue
                c2 = change[i-c1]
                if c2 >= 0:
                    if change[i] == -1:
                        change[i] = c2+1
                    else:
                        change[i] = min(c2+1, change[i])
        return change[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [float('inf')] * (amount +1)
        dp[0] = 0
        for i in coins:
            for j in range(i , amount +1):
                if dp[j-i] != float('inf'):
                    dp[j] = min(dp[j], dp[j-i]+1)
        return -1 if dp[amount] == float('inf') else dp[amount]
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount==0:
            return 0
        
        
        if len(coins)==1:
            
            if amount%coins[0]==0:
                return amount//coins[0]
            return -1
        
        dp=[0]*(amount+1)
        
        for i in range(1,amount+1):
            
            if i in set(coins):
                dp[i]=1
                continue
                
            min_coins=float('inf')
            
            for coin in coins:
                
                if i-coin>=0:
                    min_coins=min(dp[i-coin],min_coins)
                    
            dp[i]=min_coins+1
            
            
        if dp[amount]==float('inf'):
            return -1
        return dp[amount]
                    
    
    
                                 
                   
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        amount_list = [i for i in range(amount+1)]
        
        for i in range(amount+1):
            if i == 0:
                amount_list[i] = 0
            elif i in coins:
                amount_list[i] = 1
            else:
                temp = []
                for coin in coins:
                    if amount_list[i] - coin >= 0:
                        remainder = amount_list[i] - coin
                        min_coin = (amount_list[remainder])
                        if min_coin != -1:
                            temp.append(min_coin)
                        # print('amt: {}, coin: {}, remainder: {}, min coins {}'.format(amount_list[i], coin, remainder, min_coin))
                if len(temp) == 0:
                    amount_list[i] = -1
                else:
                    amount_list[i] = min(temp) + 1
                # print(amount_list)
                    
        return amount_list[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        ary = [amount+1] * (amount + 1)
        ary[0] = 0
        print(ary)
        for i in range(1, len(ary)):
            for coin in coins:
                if i-coin < 0:
                    continue
                ary[i] = min(ary[i], ary[i-coin] + 1)
        
        return ary[amount] if ary[amount] < amount + 1 else -1


class Solution:
  def coinChange(self, coins, amount):
    coins.sort(reverse = True)
    lenc, self.res = len(coins), 2**31-1
    
    def dfs(pt, rem, count):
        if not rem:
            self.res = min(self.res, count)
        for i in range(pt, lenc):
            if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                dfs(i, rem-coins[i], count+1)

    for i in range(lenc):
        dfs(i, amount, 0)
    return self.res if self.res < 2**31-1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        
        def _fewest(cs, amt):
            nonlocal memo
            if amt not in memo:
                if amt < 0:
                    memo[amt] = -1
                elif amt == 0:
                    memo[amt] = 0
                else:
                    fewest = -1
                    for c in cs:
                        f = _fewest(cs, amt-c)
                        if f != -1 and (fewest == -1 or f+1 < fewest):
                            fewest = f+1
                    memo[amt] = fewest
            return memo[amt]
        
        return _fewest(coins, amount)

class Solution:
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        mem = {}
        mem[0] = 0
        
        def rec(amount):
            if amount < 0:
                return -1
            if amount in mem:
                return mem[amount]
            m = math.inf
            for c in coins:
                n = rec(amount-c)
                if n != -1 and n+1 < m :
                    m = n+1
            if m == math.inf:
                m = -1
            mem[amount] = m
            return m
         
        
        if amount == 0:
            return 0
        return rec(amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [0] + [float('inf') for _ in range(amount)]
        
        for i in range(1, amount + 1):
            for coin in coins:
                if i - coin >= 0:
                    dp[i] = min(dp[i], dp[i-coin]+1)
    
        return -1 if dp[-1] == float('inf') else dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [sys.maxsize for i in range(amount+1)]
        
        m = len(coins)
        dp[0] = 0
        
        for j in range(m):
            for i in range(amount+1):
                if coins[j] <= i:
                    dp[i] = min(dp[i],dp[i-coins[j]] +1)
                    
        # print(dp)
        if dp[-1] == sys.maxsize:
            return -1
        return dp[-1]
            
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount:
            dp = [float('inf')]*amount
            for j in range(amount):
                if j + 1 in coins:
                    dp[j] = 1
                else:
                    for c in coins:
                        if j - c + 1 > 0:
                            dp[j] = min( dp[j], dp[j-c] + 1 )
            if dp[amount-1] < float('inf'):
                return dp[amount-1]
            else:
                return -1
        else:
            return 0

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # i = coin index
        # c[i] = coin cost
        # f(0,0) = 0
        # f(coin, amount) = minimum number of coins needed to pay amount
        # f(i, j) = min( f(i-1, j), f(i, j-c[i]) + 1)
        
        dp = [amount+1] * (amount+1)

        
        dp[0] = 0
        
        
        for i in range (len(coins)):
            coin = coins[i]
            for j in range(1, amount + 1):
                sameCoin = amount + 1
                if j - coin >= 0:
                    dp[j] = min(dp[j-coin] +1, dp[j])
                
                
        
        return dp[amount] if dp[amount] != amount + 1 else -1
    
#         rs = [amount+1] * (amount+1)
#         rs[0] = 0
#         for i in range(1, amount+1):
#             for c in coins:
#                 if i >= c:
#                     rs[i] = min(rs[i], rs[i-c] + 1)

#         if rs[amount] == amount+1:
#             return -1
#         return rs[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for i in range(n):
                if j >= coins[i]:
                    dp[j] = min(dp[j], dp[j-coins[i]]+1)
        return dp[-1] if dp[-1] < float('inf') else -1
'''
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int Max = amount + 1;
        vector<int> dp(amount + 1, Max);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.size(); j++) {
                if (coins[j] <= i) {
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
};
'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        amount_list = [i for i in range(amount+1)]
        
        for i in range(amount+1):
            if i == 0:
                amount_list[i] = 0
            elif i in coins:
                amount_list[i] = 1
            else:
                temp = []
                for coin in coins:
                    if amount_list[i] - coin >= 0:
                        remainder = amount_list[i] - coin
                        min_coin = (amount_list[remainder])
                        if min_coin != -1:
                            temp.append(min_coin)
                if len(temp) == 0:
                    amount_list[i] = -1
                else:
                    amount_list[i] = min(temp) + 1
                    
        return amount_list[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1]*(amount + 1) # max number is when we use coin 1, there is no smaller coin
        dp[0] = 0
        for partial_amount in range(1, amount + 1):
            for j in range(len(coins)):
                if coins[j] <= partial_amount:
                    dp[partial_amount] = min(dp[partial_amount], dp[partial_amount - coins[j]] + 1)
        return dp[amount] if dp[amount] <= amount else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [float('inf')] * amount
        coinValues = collections.defaultdict(list)
        for coin in coins:
            for i in range(coin, amount+1):
                if dp[i-coin] + 1 < dp[i]:
                    dp[i] = dp[i-coin] + 1
                    coinValues[i] = coinValues[i-coin] + [coin]
        print(coinValues[amount])
        return dp[-1] if dp[-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for i in range(amount + 1)]
        dp[0] = 0
        for i in range(amount + 1):
            for coin in coins:
                if i - coin >= 0:
                    dp[i] = min(dp[i], dp[i - coin] + 1)
        
        return dp[amount] if dp[amount] != float('inf') else -1

'''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first i coins with sum of j
        # dp[i][j] = min(dp[i-1][j], dp[i][j-k]+1) for k in coins
        dp = [0] + [float('inf')] * amount
        for c in coins:
            for j in range(c, amount+1):
                dp[j] = min(dp[j], dp[j-c]+1)
        return dp[-1] if dp[-1] < float('inf') else -1


        

'''    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for i in range(n):
                if j >= coins[i]:
                    dp[j] = min(dp[j], dp[j-coins[i]]+1)
        return dp[-1] if dp[-1] < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        num_ways = (amount+1) * [-1]
        num_ways[0] = 0
        
        for v in range(1, amount+1):
            fewest = float('inf')
            for c in coins:
                if v-c >= 0 and num_ways[v-c] != -1:
                    fewest = min(num_ways[v-c], fewest)
            if fewest != float('inf'):
                num_ways[v] = 1 + fewest
        
        return num_ways[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def helpCoins(coins, amount, cache):
            if amount < 0:
                return -1
            if amount == 0:
                return 0
            
            if amount in cache:
                return cache[amount]
               
            curr_min = 999
            for coin in coins:
                to_go = helpCoins(coins, amount - coin, cache)
                if to_go >= 0 and to_go < curr_min:
                    curr_min = to_go + 1
            
            if curr_min == 999:
                cache[amount] = -1
            else:
                cache[amount] = curr_min
            
            return cache[amount]
        
        return helpCoins(coins, amount, {})
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [sys.maxsize-1] * (amount+1)
        dp[0]=0
        for i in range (amount+1):
            #if dp[i] == sys.maxsize-1:
                #continue
            for c in coins:
                if c +i<=amount:
                    dp[c+i]=min(dp[c+i],dp[i]+1)
        if dp[-1] == sys.maxsize-1:
            return -1
        return dp[-1]

import collections
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        seen = set()
        queue = collections.deque()
        for coin in coins:
            queue.append((amount - coin, 1))
        
        while queue: 
            curr = queue.popleft()
            if curr[0] == 0: return curr[1]
            if curr[0] < 0 or curr[0] in seen: continue
            seen.add(curr[0])
            for coin in coins:
                queue.append((curr[0] - coin, curr[1] + 1))   
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] first j coins with sum of i
        # dp[i][j] = min(dp[j-coins[k][k]]+1) for k in coins
        n = len(coins)
        dp = [0] + [float('inf')] * amount
        for j in range(1, amount+1):
            for i in range(n):
                if j >= coins[i]:
                    dp[j] = min(dp[j], dp[j-coins[i]]+1)
        return dp[-1] if dp[-1] < float('inf') else -1
'''    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        @lru_cache(None)
        def dp(remain, cnt):
            if remain == 0:return cnt 
            ans = float('inf')
            for c in coins:
                if c*(ans-cnt)>remain>=c: 
                    ans = min(ans, dp(remain-c, cnt+1))
            return ans 
        ans = dp(amount, 0) 
        return ans if ans < float('inf')  else -1
    
    
    def coinChange(self, coins, amount):
        coins.sort(reverse = True)
        lenc, self.res = len(coins), 2**31-1

        def dfs(pt, rem, count):
            if not rem:
                self.res = min(self.res, count)
            for i in range(pt, lenc):
                if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                    dfs(i, rem-coins[i], count+1)

        for i in range(lenc):
            dfs(i, amount, 0)
        return self.res if self.res < 2**31-1 else -1
'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1] * (amount + 1)
        dp[0] = 0
        for i in range(amount+1):
            for j in range(len(coins)):
                if coins[j] <= i:
                    dp[i] = min(dp[i], 1 + dp[i-coins[j]])
                
        if dp[amount] > amount:
            return -1
        return dp[amount]
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        memo = {}
        def helper(target, options):
            if(target in memo):
                return memo[target]
            else:
                best = None
                for opt in options:
                    if opt == target:
                        memo[target] = 1
                        return 1
                    elif opt < target:
                        cont = helper(target-opt, options)
                        if cont is not None and (best is None or cont+1 < best):
                            best = cont+1
                memo[target] = best
                return best
            
        output = helper(amount, coins)
        return output if output is not None else -1


class Solution:
    def util(self, memo, coins, amount):
        if amount == 0:
            return 0
        if amount < 0:
            return -1
        minarr = []
        for coin in coins:
            if (amount - coin) not in memo:
                memo[(amount-coin)] = self.util(memo, coins, amount - coin)
            minarr.append(memo[(amount-coin)])
        
        minarr = [m for m in minarr if m != - 1]
        if not minarr:
            return -1
        return min(minarr) + 1
        
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        return self.util(memo, coins, amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 0:
            return -1
        Max = amount + 1
        dp = [Max] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount + 1):
            for j in range(len(coins)):
                if (coins[j] <= i):
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)
        
        if amount < dp[amount]:
            return -1
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
            
        
        D = [0] * (amount+1)
        
        def helper(rem):
            nonlocal D
            
                        
            if rem < 0:
                return -1
            
            if rem == 0:
                return 0
            
            if D[rem]!= 0:
                return D[rem]
            
            minn = float('inf')

            for coin in coins:
                res = helper(rem-coin)
                if res >= 0 and res < minn:
                    minn = res+1
            
            if minn == float('inf'):
                D[rem] = -1
            else:
                D[rem] = minn
                
            return D[rem]
            
        
        return helper(amount)
        
                
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1) # dp[n] means minimal coin number to gather n amount of money
        dp[0] = 0
        
        for i in range(len(dp)):
            for j in range(len(coins)):
                if i >= coins[j]:
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)
        return dp[amount] if dp[amount] < amount + 1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
              
        dp = [None for i in range(0,amount+1)]
        dp[0] = 0
    
    
        for i in range(1, amount+1):
            min_cost = float('inf')
            for j in range(0,len(coins)):
                if coins[j]<=i:
                    min_cost = min(min_cost, dp[i-coins[j]]+1)
            dp[i] = min_cost

        if dp[-1] > amount:
            return -1

        return dp[-1]

        
        coinsNeeded = {}
        coinsNeeded[0] = 0
        for n in coins:
            coinsNeeded[n] = 1
        
        
        def findMinCoins(amount):
            # print(amount)
            if amount < 0:
                return float('inf')
            
            if amount in coinsNeeded:
                # coinsNeeded[amount] = min(coinsUsed, coinsNeeded[amount])
                return coinsNeeded[amount]
            
            for n in coins:
                # if amount-n>=0:
                coinsUsed = 1+findMinCoins(amount-n)
                # if coinsUsed > 0:
                if amount in coinsNeeded:
                    coinsNeeded[amount] = min(coinsUsed, coinsNeeded[amount])
                else:
                    coinsNeeded[amount] = coinsUsed


            # if amount in coinsNeeded:
            return coinsNeeded[amount]
            # return -1
        
        findMinCoins(amount)
        # print(coinsNeeded)
        if coinsNeeded[amount] == float('inf'):
            return -1
        # if amount in coinsNeeded:
        return coinsNeeded[amount]
        # return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        import sys
        dp = [sys.maxsize for _ in range(amount + 1)]
        dp[0] = 0
        for i in range(0,amount + 1):
            
            for coin in coins:
                
                if 0 <= i - coin < amount + 1:
                    
                    dp[i] = min(dp[i], dp[i-coin] + 1)
        return dp[-1] if dp[-1] != sys.maxsize else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0
        coins.sort()
        for i in range(amount + 1):
            for j in range(len(coins)):
                if(coins[j] <= amount):
                    dp[i] = min(dp[i], 1 + dp[i - coins[j]])
                else:
                    break
        if(dp[amount] > amount):
            return -1
        else:
            return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1 for i in range(amount+1)]
        dp[0] = 0
        for i in range(1,amount+1):
            for j in range(len(coins)):
                if coins[j]<=i:
                    dp[i] = min(dp[i], dp[i-coins[j]] + 1)
        return -1 if dp[-1] > amount else dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp=[float('inf')]*(amount+1)
        dp[0]=0
        
        for i in range(1,amount+1):
            for j in range(len(coins)):
                if coins[j]<=i:
                    dp[i] = min(dp[i],dp[i-coins[j]]+1)

        print(dp)
        return dp[amount] if dp[amount]<float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [sys.maxsize] * (amount+1)
        dp[0] = 0
        for i in range(1, amount+1):
            for c in coins:
                if 0 <= i-c < amount+1:
                    dp[i] = min(dp[i], dp[i-c]+1)
        return dp[-1] if dp[-1] != sys.maxsize else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1 for _ in range(amount + 1)]
        dp[0] = 0;
        for i in range(1, amount + 1):
            for j in range(len(coins)):
                if coins[j] <= i:
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)
        return -1 if dp[amount] > amount else dp[amount]

import collections
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        seen = set()
        queue = collections.deque()
        for coin in coins:
            queue.append((amount - coin, 1))
        
        while queue: 
            curr = queue.popleft()
            if curr[0] == 0: return curr[1]
            if curr[0] < 0 or curr[0] in seen: continue
            seen.add(curr[0])
            for coin in coins:
                queue.append((curr[0] - coin, curr[1] + 1))
        
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coins = coins
        self.amount = amount
        self.cache = {}
        
        # Run
        res = self.getValue(self.amount)
        if res is None:
            return -1
        else:
            return res
        
        
        
    def getValue(self, amount):
        if amount in self.cache:
            return self.cache[amount]
        if amount < 0:
            return None
        if amount == 0:
            return 0
        if amount in self.coins:
            return 1
        # Recurse
        min_count = None
        for coin in self.coins:
            count = self.getValue(amount - coin)
            if count is not None:
                if min_count is None or min_count > count + 1:
                    min_count = count + 1
        self.cache[amount] = min_count
        return min_count


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        s = list()
        import sys
        for i in range(0,amount+1):
            s.append(sys.maxsize)
        s[0] = 0
        
        for i in range(1,amount+1):
            m = sys.maxsize
            for j in range(0, len(coins)):
                if i-coins[j] >= 0:
                    m = min(m, s[i-coins[j]])
            if m == sys.maxsize:
                s[i] = m
            else:
                s[i] = m+1
        if s[amount] == sys.maxsize:
            s[amount] = -1
        
        return s[amount]
                    
        


class Solution:
    def coinChange(self, coins, amount):
        '''
        :type coins: list of int
        :type amount: int
        :rtype: int
        '''
        dp = [amount + 1] * (amount + 1)
        dp[0] = 0

        for i in range(1, amount + 1):
            for j in range(0, len(coins)):
                if coins[j] <= i:
                    dp[i] = min(dp[i], dp[i - coins[j]] + 1)

        return -1 if dp[amount] > amount else dp[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1 for _ in range(amount+1)]
        dp[0] = 0
        coins.sort()
        for i in range(amount+1):
            for j in range(len(coins)):
                if coins[j] <= i:
                    dp[i] = min(dp[i], 1+dp[i-coins[j]])
                else:
                    break
                    
        if dp[-1] == amount + 1:
            return -1
        else:
            return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 0 or not coins:
            return -1
        if amount == 0:
            return 0
        min_com = [0 for i in range(amount+1)]
        for i in range(1, amount + 1):
            for c in coins:
                if i >= c and (min_com[i - c] > 0 or i - c == 0):
                    if min_com[i] == 0:
                        min_com[i] = min_com[i - c] + 1
                    else:
                        min_com[i] = min(min_com[i], min_com[i - c] + 1)
            # if min_com[i] == 0:
                # min_com[i] = -1
        if min_com[amount] == 0:
            return -1
        return min_com[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.smallest = float('inf')
        coins.sort(reverse=True)
        self.dfs(coins, amount, 0)
        return -1 if type(self.smallest) is float else self.smallest
    
    def dfs(self, coins, amount, prev_count):
        if len(coins) == 0:
            return
        if amount % coins[0] == 0:
            self.smallest = min(self.smallest, prev_count + amount // coins[0])
        else:
            for k in range(amount // coins[0], -1, -1):
                if prev_count + k >= self.smallest:
                    break
                self.dfs(coins[1:], amount - k * coins[0], prev_count + k)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount + 1 for i in range(amount + 1)]
        dp[0] = 0
        coins.sort()
        for i in range(amount + 1):
            for j in range(len(coins)):
                if(coins[j] <= amount):
                    dp[i] = min(dp[i], 1 + dp[i - coins[j]])
                else:
                    break
        if(dp[amount] > amount):
            return -1
        else:
            return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount==0:
            return 0
        dp=[None]*(amount+1)
        dp[0]=0
        n= len(coins)
        for coin in coins:
            if coin<= amount:
                dp[coin]=1
        for i in range(1,amount+1):
            if i in coins:
                continue
            tmp= float('inf')
            for coin in coins:
                
                if coin<= i:
                    if dp[i-coin]==-1:
                        continue
                    else:                        
                        tmp= min(tmp,1+dp[i-coin])
                else:
                    continue
            if tmp==float('inf'):
                dp[i]=-1
            else:
                dp[i]=tmp
        print(dp)        
        return dp[-1]
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:    
        dp = [0] + [float('inf') for i in range(amount)]
        for cost in range(0, len(dp)):
            for coin in coins:
                if cost + coin > amount:
                    continue
                dp[cost + coin] = min(dp[cost + coin], dp[cost] + 1)
        if dp[-1] != float('inf'):
            return dp[-1]
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:

        def make_change(sum_remaining, coins, mem):
            if sum_remaining < 0:
                return -1
            elif sum_remaining == 0:
                return 0
            elif mem[sum_remaining]:
                return mem[sum_remaining]
            else:
                min_coins = amount + 1
                for coin in coins:
                    if (sum_remaining - coin) >= 0:
                        prev_coins = make_change(sum_remaining - coin, coins, mem)
                        curr_coins = 1 + prev_coins
                        if curr_coins > 0 and curr_coins < min_coins:
                            min_coins = curr_coins
                mem[sum_remaining] = min_coins
                return mem[sum_remaining]
        
        mem = [None] * (amount+1)
        ans = make_change(amount, coins, mem)
        if ans == amount + 1:
            return -1
        else:
            return ans

# top down approach
import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        counts = [0]*amount
        return f(coins, amount, counts)

def f(coins, amount, counts):
    
    # base case
    if amount < 0:
        return -1
    if amount == 0:
        return 0
    if (counts[amount - 1] != 0):
        return counts[amount - 1]
    min_val = math.inf
    for c in coins:
        res = f(coins, amount - c, counts)
        if res >= 0 and res < min_val:
            min_val = 1 + res
    
    if min_val == math.inf:
        counts[amount - 1] = -1
    else:
        counts[amount - 1] = min_val
    
    return counts[amount - 1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        changes=[amount+1]*(amount+1)
        changes[0]=0
        for i in range(1,amount+1):
            for coin in coins:
                if coin>i:
                    break
                else:
                    changes[i]=min(changes[i],changes[i-coin]+1)
        return changes[-1] if changes[-1]!=(amount+1) else -1

class Solution:
    def coinChange(self, coins, amount):
        coins.sort(reverse = True)
        lenc, self.res = len(coins), 2**31-1

        def dfs(pt, rem, count):
            if not rem:
                self.res = min(self.res, count)
            for i in range(pt, lenc):
                if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                    dfs(i, rem-coins[i], count+1)

        for i in range(lenc):
            dfs(i, amount, 0)
        return self.res if self.res < 2**31-1 else -1

#       def coinChange(self, coins: List[int], amount: int) -> int:
#         coins.sort(reverse = True)
#         min_coins = float('inf')

#         def count_coins(start_coin, coin_count, remaining_amount):
#             nonlocal min_coins

#             if remaining_amount == 0:
#             min_coins = min(min_coins, coin_count)
#             return

#           # Iterate from largest coins to smallest coins
#             for i in range(start_coin, len(coins)):
#             remaining_coin_allowance = min_coins - coin_count
#             max_amount_possible = coins[i] * remaining_coin_allowance

#             if coins[i] <= remaining_amount and remaining_amount < max_amount_possible:
#                 count_coins(i, coin_count + 1, remaining_amount - coins[i])

#         count_coins(0, 0, amount)
#         return min_coins if min_coins < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:

        @lru_cache(maxsize=None)
        def helper(amount):
            if amount <= 0:
                return 0
            min_so_far = math.inf
            for i in range(len(coins)):
                if coins[i] <= amount:
                    min_so_far = min(min_so_far, 1 + helper(amount - coins[i]))
            return min_so_far

        result = helper(amount)
        if result == math.inf:
            return -1
        return result

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        T = len(coins)
        dp = [0 for i in range(amount + 1)]
        for n in range(1, amount + 1):
            dp[n] = math.inf
            for i in range(T):
                if (n - coins[i] >= 0):
                    subProbSol = dp[n - coins[i]]
                    dp[n] = min(dp[n], subProbSol + 1)
        if dp[amount] == math.inf : return -1
        else : return (dp[amount])


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = {}
        dp[0]=0
        for i in range(1, amount+1):
            dp[i] = float('inf')
            for c in coins:
                if c<i:
                    dp[i] = min(dp[i-c]+1, dp[i])
                if c==i:
                    dp[i]=1
        
        return dp[amount] if dp[amount] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        numCoins = [float('inf') for i in range(amount)]
        for coin in coins:
            if coin-1 < amount:
                numCoins[coin-1]=1
        for i in range(amount):
            minCoin = float('inf')
            change=True
            for coin in coins:
                if i == coin-1:
                    change = False
                    break
                if i-coin>=0:
                    minCoin = min(numCoins[i-coin], minCoin)
            if change and minCoin !=float('inf'):
                numCoins[i]=minCoin+1
            # print(numCoins)
        if numCoins[-1]==float('inf'):
            return -1
        return numCoins[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        11 -> 0
        
        10 -> 1 -> (6 -> 5)
        9 -> 1
        6 -> 1
        
        
        '''
        
        def helper(remAmt, coins):
            nonlocal mem
            
            if remAmt in mem:
                return mem[remAmt]

            minCoins = float('inf')

            for coin in coins:
                if coin <= remAmt:
                    result = 1 + helper(remAmt-coin, coins)
                    minCoins = min(minCoins, result)
                else:
                    break
            
            mem[remAmt] = minCoins
            return minCoins
        
        if amount < 1:
            return 0
        
        mem = {0:0}
        coins.sort()
        minCoins = helper(amount, coins)
        if minCoins == float('inf'):
            return -1
        return minCoins

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        return self.aux(coins, amount, memo)
        
    def aux(self, coins, amount, memo) :
        if amount < 0 :
            return -1
        
        if amount == 0 :
            return 0
        
        if amount in memo.keys() :
            return memo[amount]
        
        minimum = 999999999
        
        for x in coins :
            temp = self.aux(coins, amount - x, memo)
            if temp < minimum and temp >= 0 :
                minimum = temp
                
        if minimum == 999999999 :
            memo[amount] = -1
            return memo[amount]
            
        memo[amount] = 1 + minimum
        
        return memo[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = set(coins)        
        d = {}
        if(amount == 0):
            return 0
        
        def solve(amt):
            if amt in d:
                return d[amt]
            
            if amt <= 0:
                return -1
            
            if amt in coins:
                d[amt] = 1
                return d[amt]
            
            poss = []
            
            for c in coins:
                search = amt-c
                if(search <1):
                    continue
                val = solve(search)
                if val != -1:
                    poss.append(val)
            
            if(len(poss) != 0):
                d[amt] = 1 + min(poss)
            else:
                d[amt] = -1
                
            return d[amt]                
        
        return solve(amount)
            
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        self.cache=[0] * (amount + 1)
        return self.coin_change(coins, amount)

    def coin_change(self, coins, remainder):
        if remainder < 0:
            return -1

        if remainder == 0:
            return 0

        if self.cache[remainder] != 0:
            return self.cache[remainder]

        system_max = sys.maxsize
        minimum = system_max
        for coin in coins:
            change_result = self.coin_change(coins, remainder - coin)

            if (change_result >= 0 and change_result < minimum):
                minimum = 1 + change_result

        self.cache[remainder] = -1 if (minimum == system_max) else minimum

        return self.cache[remainder]
#         if not coins:
#             return 0
#         dp = [float(\"inf\") for _ in range(amount + 1)]
        
#         dp[0]=0
#         for i in range(1,amount+1):
#             for coin in coins:
#                 if i>=coin:
#                     dp[i] = min(dp[i],dp[i-coin]+1)
#         if dp[amount]!= float(\"inf\"):
#             return dp[-1]
#         else:
#             return -1
            
#         \"\"\"
#         #1  Return 0 if there are no given coins
#         \"\"\"
#         if not coins:
#             return 0
        
#         \"\"\"
#         #2  Initialize a 'dp' array of a size of the given amount (inclusive, hence +1).
#         #   Initialize each index element to be an arbitary number. In this case I chose amount + 1. E.g.: 11 + 1 = 12
#         \"\"\"
#         dp = [float(\"inf\") for _ in range(amount + 1)]
        
#         \"\"\"
#         #3  Base case. Make the first cell 0 because if amount is 0, you do not need any coin to make up to that amount
#         #   stdout: dp = [0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12]
#         \"\"\"
#         dp[0] = 0
        
#         \"\"\"
#         #4  Look through the entire 'dp' array, for each of the index element, we will check it against the list(array) of given coins.
#         #   'i' in this case is the amount we want to compute from bottom-up starting from 0 to the 'amount' that the question asks for
#         #   E.g.: [0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12] vs [1, 2, 5]
#                    ^                   ^                           ^             
#                    amount 0            amount 5                    amount = 11 (this is what we want)             
#         \"\"\"
#         for i in range(1, amount+1):
#             for coin in coins:
#                 \"\"\"
#                 #5  If 'i' (the current amount we want to find, is less than 'coin', we will skip that. Otherwise compute it:
#                 #   Because it does not make sense to compute the coin if the coin itself is already larger than the amount that we're looking at
#                 \"\"\"
#                 if i >= coin:
#                     \"\"\"
#                     #6  We shall update each dp[i] to be the minimum between itself vs it's previous computed answer dp[i-coin]. 
#                     #   And we do this for each available coin. Only if 'coin' is smaller than the amount 'i'
#                     #   We do '+1' for dp[i-coin] + 1 here because it costs us a step to do so
#                     \"\"\"
#                     dp[i] = min(dp[i], dp[i-coin] + 1)
        
#         \"\"\"
#         #7  By the end of the 2 for loops, you will end up with 
#         #   stdout: [0, 1, 1, 2, 2, 1, 2, 2, 3, 3, 2, 3]
        
#         #8  So if the dp[amount] is not the original amount we initialized at Step #3. Then we shall return the dp[amount] that was asked by the question
#         \"\"\"
#         if dp[amount] != float(\"inf\"):
#             return dp[amount]
#         else:
#             return -1
        
#         \"\"\"
#         #9  Else. If the dp[amount] is still amount + 1 as we initialized. It means there were no suitable coins for this scenario and -1 is returned
#         \"\"\"
    
#         # key:amount value:min number of coins for the amount
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        return self.dfs(coins, 0, 0, amount, -1)
    
    def dfs(self, coins: List[int], idx: int, cnt: int, amount: int, minCnt: int) -> int:
        if amount==0:
            return cnt
        if idx >= len(coins):
            return -1
        
        # Trim
        if minCnt>0 and cnt+amount//coins[idx]+1 > minCnt:
            return -1
        
        # Select [0, amount//coins[idx] ]
        for i in range(amount//coins[idx], -1, -1):
            res = self.dfs(coins, idx+1, cnt+i, amount-coins[idx]*i, minCnt)
            if res!=-1:
                minCnt = res if minCnt==-1 else min(minCnt, res)
        return minCnt

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        use dp 0 - 11
        '''
        if amount == 0:
            return 0
        
        dp = [float('inf')] * amount
        
        for i in range(0, amount):
            for c in coins:
                if (i + 1) % c == 0:
                    dp[i] = min(dp[i], (i + 1) // c)
                elif i + 1 > c:
                    dp[i] = min(dp[i], dp[i - c] + 1)
            
        return -1 if dp[-1] == float('inf') else dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        if amount==0: return 0
        if amount<min(coins):
            return -1
        
        l = min(coins)
        
        A = [0 for i in range(amount+1)]
        A[l] = 1
        
        for i in range(1,amount+1):
            A[i] = float('inf')
            for coin in coins:
                if i>coin and A[i-coin]>0:
                    A[i] = min(A[i],1+A[i-coin])
                elif i==coin: A[i] = 1
            if A[i]==float('inf'):
                A[i]=0
                           
                           
                
                
        print(A)
        if A[amount]==0: return -1
        return A[amount]
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:

        count = {}
        for coin in coins:
            count[coin] = 1

        count[0] = 0

        for i in range(1,amount+1):
            ans = amount + 1
            if i in list(count.keys()):
                continue

            for coin in coins:
                if i - coin > 0 and count[i-coin]!= -1:
                    ans = min(count[i-coin], ans)
            if ans == amount + 1:
                count[i] = -1
            else:
                count[i] = ans + 1

        return count[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1: return 0
        cnt = [0 for __ in range(amount)]
        return self.coinMake(coins, amount, cnt)
        
    def coinMake(self, coins, rem, count):
        if rem < 0: return -1
        if rem == 0: return 0
        if count[rem - 1] != 0: return count[rem - 1]
        MIN = 2**32 - 1
        for i in coins:
            res = self.coinMake(coins, rem - i, count)
            if res >= 0 and res < MIN: MIN = 1 + res
        count[rem - 1] = -1 if MIN == 2**32 - 1 else MIN
        return count[rem - 1]
        
        #recursive + memo
        #memo = [2**32 - 1 for __ in range(amount + 1)]
        #memo[0] = 0
        #return self.coinChangeDFS(coins, amount, memo)
    
    #def coinChangeDFS(self, coins, target, memo):
    #    if target < 0: return -1
    #    if memo[target] != 2**32 -1: return memo[target]
    #    for i in range(len(coins)):
    #        tmp = self.coinChangeDFS(coins, target - coins[i], memo)
    #        if tmp >= 0: memo[target] = min(memo[target], tmp + 1)
    #    return -1 if memo[target] == 2**32 - 1 else memo[target]
        
        
        
        #iterative
        #DP = [amount + 1 for __ in range(amount + 1)]
        #DP[0] = 0

        #for i in range(amount + 1):
        #    for j in range(len(coins)):
        #        if coins[j] <= i:
        #            DP[i] = min(DP[i], DP[i - coins[j]] + 1)
        #return -1 if DP[amount] > amount else DP[amount]
        
        
        
        
        
        
        
        



class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        
        mem = {}
        max_amount = 10 ** 4 + 1
        
        def cc(amount):
            mem[amount] = max_amount
            for coin in coins:
                dif = amount - coin
                
                if dif == 0:
                    mem[amount] = 1
                elif dif > 0:
                    if dif not in mem:
                        cc(dif)
                    mem[amount] = min(mem[amount], mem[dif] + 1)
        
        cc(amount)
        
        if mem[amount] == max_amount:
            return -1
        else:
            return mem[amount]
                    
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        states = [0] * (amount + 1)
        for s in range(0, amount+1):
            if s == 0:
                states[s] = 0
            else:
                min_c = 1e9
                for c in coins:
                    if c <= s and states[s-c] != -1:
                        min_c = min(min_c, 1 + states[s-c])
                if min_c == 1e9:
                    states[s] = -1
                else:
                    states[s] = min_c
                print(s, states[s])
        return states[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0 or not coins:
            return 0
        
        minlst = [float('inf')] * (amount + 1)
        n = len(minlst)
        minlst[0] = 0

        for i in range(1,n):
            for denom in coins:
                remainder = i - denom
                if remainder < 0:
                    continue

                newsmallest = min(minlst[remainder] + 1, minlst[i])
                minlst[i] = newsmallest
        return -1 if minlst[-1] == float('inf') else minlst[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        s = list()
        #import sys
        #for i in range(0,amount+1):
        #    s.append(sys.maxsize)
        #s[0] = 0
        s.append(0)
        
        for i in range(1,amount+1):
            m = list()
            for j in range(0, len(coins)):
                if i-coins[j] >= 0:
                    m.append(s[i-coins[j]])
            #print(m)
            #remove negative 
            mm = [item for item in m if item >= 0]
            #print(mm)
            if len(mm) == 0 :
                s.append(-1)
            else:
                s.append(min(mm)+1)
        #if s[amount] == sys.maxsize:
        #    s[amount] = -1
        
        return s[amount]
                    
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def my_coinChange(coins, rem, count):
            if rem < 0:
                return -1
            if rem == 0:
                return 0
            if count[rem - 1] != 0:
                return count[rem - 1]

            my_min = float('inf')

            for coin in coins:
                res = my_coinChange(coins, rem - coin, count)
                if res >= 0 and res < my_min:
                    my_min = 1 + res

            count[rem - 1] = -1 if my_min == float('inf') else my_min
            return count[rem - 1]
        
        if amount < 1:
            return 0
        return my_coinChange(coins, amount, [0] * amount)
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1

        f = [sys.maxsize for i in range(amount + 1)]

        f[0] = 0
        for i in range(1, len(f)):
            for coin in coins:
                if i - coin >= 0 and f[i - coin] != sys.maxsize:
                    f[i] = min(f[i], f[i - coin] + 1)

        if f[amount] == sys.maxsize:
            return -1

        return f[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [None for _ in range(amount+2)]
        dp[amount+1] = float('inf')
        dp[amount] = 0
        for i in range(amount-1, -1, -1):
            s = float('inf')
            for c in coins:
                s = min(s, 1 + dp[min(c+i,amount+1)])
            dp[i] = s
        if dp[0] == float('inf'):
            return -1
        return dp[0]
        
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def helper(coins, rem, count): #count is a list
            if rem < 0:
                return -1
            if rem == 0:
                return 0
            if count[rem-1] != 0: return count[rem-1]
            minVal = float('inf')
            for c in coins:
                res = helper(coins, rem-c, count)
                if res >= 0 and res < minVal:
                    minVal = 1 + res;
            if minVal == float('inf'):
                count[rem-1] = -1
            else:
                count[rem-1] = minVal
            return count[rem-1]
        
        if amount < 1:
            return 0
        return helper(coins, amount, [0]*amount)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        result = []
        coins.sort(reverse=True)
        max_sum = 2**31-1

        def find_combinations(combination, remain, start):
            nonlocal max_sum
            if remain == 0:
                max_sum = min(max_sum, len(combination))
                return
            elif remain < 0:
                return

            for i in range(start, len(coins)):
                allowed_coins = max_sum - len(combination)
                max_value_can_be_achieved = coins[i] * allowed_coins
                if coins[i] <= remain < max_value_can_be_achieved:
                    combination.append(coins[i])
                    find_combinations(combination, remain-coins[i], i)
                    combination.pop()
                
        find_combinations([], amount, 0)
        
        if  max_sum == 2**31 -1:
            return -1
        return max_sum
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        memo = {}

        def solve(cur):
            
            if cur in memo:
                return memo[cur]
            
            if cur == 0:
                return 0
            
            arr = [float('inf')] * len(coins)
            for i, coin in enumerate(coins):
                if cur - coin < 0:
                    arr[i] = float('inf')
                else:
                    arr[i] = solve(cur - coin) + 1
                
            ret = min(arr)
            memo[cur] = ret
            return ret
        
        ans = solve(amount)
        
        if ans == float('inf'):
            return -1
        return ans


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # use DP
        N = len(coins)
        M = amount
        table = [-1]*(M+1)
        for j in range(N+1):
            table[0] = 0
        for m in range(1,M+1):
            for i in range(N):
                c = coins[i]
                if c > m:
                    pass
                elif table[m-c] != -1:
                    if table[m]!= -1:
                        table[m] = min(table[m-c]+1, table[m])
                    else:
                        table[m] = table[m-c]+1
        return table[M]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def dp(t):
            if t in d:
                return d[t]
            res=float('inf')
            for c in coins:
                if t-c>=0:
                    res=min(res,dp(t-c)+1)
            d[t]=res
            return res
        d={0:0}
        x=dp(amount)
        return x if x<float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def dfs(remaining):
            nonlocal memo
            if remaining == 0:
                return 0
            if remaining < 0:
                return -1
            if memo[remaining]:
                return memo[remaining]
            
            # res initialize as infinity, but in this case
            # total amount is `amount`, we at most have `amount` 1 coins
            # so, `amount+1` can be considered as a maximum
            res = amount + 1
            for coin in coins:
                count = dfs(remaining - coin)
                if count == -1:
                    continue
                res = min(res, 1 + count)
            memo[remaining] = res if res != amount + 1 else -1
            return memo[remaining]
        memo = [None for _ in range(amount + 1)]
        return dfs(amount)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        cal=[float('inf') for i in range(amount+1)]
        for i in range(0,amount+1):
            for j in range(len(coins)):
                if i==0:
                    cal[i]=0
                elif coins[j]<=i:
                    cal[i]=min(1+cal[i-coins[j]],cal[i])
        print(cal)
        if cal[amount] == float('inf'):
            return -1
        else:
            return cal[amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
        # dp[i] is the minimum number of coins to total i + 1 dollars
        dp: List[int] = [0 for _ in range(amount)]

        for i in range(amount):
            for coin in coins:
                if coin > i + 1:
                    continue
                if coin == i + 1:
                    dp[i] = 1
                elif dp[i - coin] != 0:
                    if dp[i] == 0:
                        dp[i] = dp[i - coin] + 1
                    else:
                        dp[i] = min(dp[i], dp[i - coin] + 1)

        return dp[amount - 1] if dp[amount - 1] != 0 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        x=coinCount(coins, amount, {})
        if(x==float('inf')):
            return -1
        return x
    
    
    
def coinCount(coins, amount, found):
    
    if(amount<0):
        return float('inf')
    if(amount==0):
        return 0
    if(found.get(amount)):
        return found[amount]
    count=0
    minCount=float('inf')
    for coin in coins:
        if(coin==amount):
            return 1
        count=1+coinCount(coins, amount-coin, found)
        if(count< minCount):
            minCount=count
            
            
    found[amount]=minCount
    return minCount
    
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        dp = [-1 for i in range(amount+1)]
        dp[0] = 0
        for i in range(0, amount+1):
            for value in coins:
                if i - value > 0:
                    if dp[i-value] >= 0:
                        if dp[i] < 0:
                            dp[i] = dp[i-value]+1
                        else:
                            dp[i] = min(dp[i], dp[i-value]+1)
                if i - value == 0:
                    dp[i] = 1
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        
        def opt(v):
            if v == 0:
                return 0
            if v in memo:
                return memo[v]
            
            result = float('inf')
            for i in coins:
                if v - i >= 0:
                    result = min(result, opt(v-i)+1)
                else:
                    continue
            memo[v] = result
            return memo[v]
        
        memo = {}
        coins = opt(amount)
        if coins == float('inf'):
            return -1
        else:
            return coins

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        seen = {0:0}


        def helper(coins, amount):
            
            if amount in seen:
                return seen[amount]
            
            if amount < 0:
                return -1

            leastCoins = float('Inf')
            for coin in coins:
                next_least = helper(coins, amount-coin)
                if next_least != -1:
                    leastCoins = min(leastCoins, 1+next_least)
            if leastCoins == float('Inf'):
                seen[amount] = -1
                return -1
            else:
                seen[amount] = leastCoins
                return leastCoins
            

        return helper(coins, amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        memo = { x: amount+1 for x in range(amount+1)}
        memo[0] = 0        
        
        for i in range(amount+1):
            for j in range(len(coins)):
                if coins[j] <= i:
                    memo[i] = min( memo[i], 1 + memo[i- coins[j]])
               
        
        if memo[amount] < amount + 1:
            return memo[amount]
        else:
            return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        dp = {}
        for c in coins:
            dp[c] = 1

        for i in range(amount+1):
            for c in coins:
                if dp.get(i-c):
                    if not dp.get(i):
                        dp[i] = dp[i-c] + 1
                    else:
                        dp[i] = min(dp[i], dp[i-c] + 1)
        return dp.get(amount, -1)
                    
                


class Solution:
    IMPOSSIBLE = -1
    
    def computeMinCoins(self, n: int) -> int:
        if (result := self.memoization.get(n)) is not None:
            return result
        if n == 0:
            result = 0
        else:
            result = Solution.IMPOSSIBLE
            for coin in self.coins:
                if coin > n:
                    continue
                if ((needed_extra := self.computeMinCoins(n - coin)) !=
                        Solution.IMPOSSIBLE):          
                    possible_result = 1 + needed_extra
                    if result == Solution.IMPOSSIBLE or result > possible_result:
                        result = possible_result
        self.memoization[n] = result
        return result
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        if amount % coins[0] == 0:
            return amount // coins[0]
        self.coins = coins
        self.memoization = {}
        return self.computeMinCoins(amount)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        dp = [None for _ in range(amount + 1)]
        ret = self.helper(coins, amount, dp)
        return ret if ret != float('inf') else -1
        
    def helper(self, coins: List[int], amount: int, dp) -> int:
        if amount == 0:
            return 0
        if amount < coins[0]:
            return float('inf')
        
        ans = float('inf')
        for coin in coins:
            if amount >= coin:
                if amount == coin:
                    ans = 0
                else:
                    tmp = self.helper(coins, amount - coin, dp) if dp[amount-coin] == None else dp[amount-coin]
                    if dp[amount-coin] == None:
                        dp[amount-coin] = tmp
                        
                    ans = min(ans, tmp)
        
        return ans + 1 if ans != -1 else -1

# class Solution:
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         min_coins = float('inf')
#         def backtrack(tot, count):
#             nonlocal min_coins
#             if tot == 0:
#                 min_coins = min(min_coins, count)
#                 return
#             for coin in coins:
#                 if coin <= tot:
#                     backtrack(tot - coin, count + 1)
#         backtrack(amount, 0)
#         return -1 if min_coins == float('inf') else min_coins
    
    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        def backtrack(tot):            
            if tot == 0:
                return 0   
            if tot not in memo:
                min_coins = float('inf')
                for coin in coins:
                    if coin <= tot:
                        cur_count = backtrack(tot - coin) + 1
                        min_coins = min(min_coins, cur_count)
                memo[tot] = min_coins
            return memo[tot]
        out = backtrack(amount)
        return -1 if out == float('inf') else out


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memory = {}
        return self._helper(coins, amount, memory)
    
    def _helper(self, coins, amount, memory):
        if amount < 0:
            return -1
        elif amount == 0:
            return 0
        elif amount in memory:
            return memory[amount]
        
        potential_values = []
        for coin in coins:
            new_target = amount - coin
            min_coins = self._helper(coins, new_target, memory)
            
            if min_coins != -1:
                potential_values.append(min_coins)
                
        if len(potential_values) == 0:
            memory[amount] = -1
        else:
            memory[amount] = min(potential_values) + 1
        
        return memory[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        d = {0:0}
        
        def dfs(left, num):
            if left < 0:
                return -1
            
            if left in d:
                return d[left]
            
            min_ans = math.inf
            for each in coins:
                val = dfs(left-each, num+1)
                if val >= 0:
                    min_ans = min(min_ans, val)
            
            if min_ans == math.inf:
                d[left] = -1
            else:
                d[left] = min_ans + 1
            
            return d[left]
        

        return dfs(amount, 0)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] * (amount + 1)
        return self.painful(coins, amount, dp)
        
    def painful(self, coins: List[int], amount: int, dp:List[int]) -> int:
        if amount == 0:
            return 0
        
        if amount < 0:
            return -1
        
        if dp[amount]:
            return dp[amount]

        minCost = math.inf
            
        for coin in coins:
            res = self.painful(coins, amount - coin, dp)
            
            if res != -1:
                minCost = min(minCost, 1 + res)
        
        dp[amount] = minCost if minCost < math.inf else -1
            
        return minCost if minCost < math.inf else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        cache = [[float('inf')]*(amount + 1) for _ in range(len(coins) + 1)]
        # for any amount, no coins
        for i in range(len(coins) + 1):
            cache[i][0] = 0
            
        for i in range(1, len(coins) + 1):
            for j in range(1, amount + 1):
                if j >= coins[i - 1]:
                    cache[i][j] = min(cache[i - 1][j], cache[i][j - coins[i - 1]] + 1)
                else:
                    cache[i][j] = cache[i - 1][j]

        return cache[-1][-1] if cache[-1][-1] != float('inf') else -1


    # Recurrence Relation
    # base case: no coins, any amount > 0: -1
    #            any number of coins (even 0), amount 0: 0 coins
    #
    # For any new coin i, either use the coin or don't use it to reach the amount j
    # If not using coin: num_coins[i][j] = num_coins[i - 1][j] same as num coins without that coin
    # If using coin: num_coins[i][j] = num_coins[i][j - coins[i]] + 1 num coins with (or without) that coin and an amount (amount - coin_value) plus 1
    # Take minimum between using/not using coin
    
    # Can build this up from no coins at all
    #      amount
    # coins , , , ,


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def helper(amount, minAmounts):
            if amount == 0:
                return 0
            elif amount < 0:
                return math.inf
            if amount in minAmounts:
                return minAmounts[amount]
            minAmount = math.inf
            for coin in coins:
                minAmount = min(minAmount, helper(amount-coin, minAmounts)+1)
            minAmounts[amount] = minAmount
            return minAmount
        
        minAmounts = dict()
        x = helper(amount, minAmounts)
        if x == math.inf:
            return -1
        return x


class Solution:
    def __init__(self):
        self.cache = dict()
        self.coin_set = set()

    # some key takeaways again:
    # need to reverse sort because of the problem - trying to find min, so need to cache for min
    # because if get a cache hit then it won't search for more, even if it's not min
    
    # the dfs structure here helps because doing a min over a for loop
    def helper(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        if amount in self.cache:
            return self.cache[amount]
        if amount in self.coin_set:
            return 1
        potentials = [float('inf')]
        for coin in coins:
            if amount > coin:
                potentials.append(self.helper(coins,amount - coin))
        res = 1 + min(potentials)
        self.cache[amount] = res
        return res

    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coin_set = set(coins)
        coins = sorted(coins,reverse=True)
        res = self.helper(coins,amount)
        if res == float('inf'):
            return -1
        return res
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins and amount==0: return 1
        if not coins: return 0
        
        dp = [float('inf')]*(amount+1) 
        dp[0]=0
        for i in range(len(coins)+1):
            for j in range(amount+1):
                if j-coins[i-1]>=0:
                        dp[j] = min(dp[j], dp[j-coins[i-1]]+1)         
        return dp[-1] if dp[-1]!=float('inf') else -1



class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        coins.sort()
        cache = {}
        
        def rec(amount):
            
            if amount == 0:
                return 0
            if amount in cache:
                return cache[amount]
            val = float('inf')
            
            for coin in coins:
                
                if amount-coin >= 0:
                    val = min(val,1+rec(amount-coin))
            cache[amount] = val
            return val
            
        ans = rec(amount)
        return ans if ans != float('inf') else -1
                    


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 


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        cache = {0: 0}
        
        def recurse(i):
            if i in cache: return cache[i] 
            
            n = i+1
            for coin in coins:
                curr = 0
                if i >= coin:
                    next_amount = recurse(i - coin)
                    if next_amount >= 0:
                        curr = 1 + next_amount
                if curr > 0:
                    n = min(n, curr)
            
            result = -1 if n == i+1 else n
            cache[i] = result
            return result
        
        return recurse(amount)
                    


class Solution:
    
    
    
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        def helper(coins, amount, dp):
            if amount < 0:
                return float('inf')
            if amount == 0:
                return 0
            if amount in dp:
                return dp[amount]
            ans = []
            for i in range(len(coins)):
                use_ci = 1 + helper(coins, amount - coins[i], dp)
                ans.append(use_ci)
                
            dp[amount] = min(ans)
 
            return dp[amount]
        
        
        
        if amount <= 0:
            return 0
        dp = {}
        result = helper(coins, amount, dp)
        return -1 if result == float('inf') else result
    



from collections import defaultdict

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def coin_change_for_amount(coins, amount):
            nonlocal count_amount
            
            if amount < 0:
                return -1
            
            if amount == 0:
                return 0
            
            if count_amount[amount] != float('inf'):
                return count_amount[amount]
            
            min_coins_amt = float('inf')
            for coin in coins:
                
                min_coins_amt_coin = coin_change_for_amount(coins, amount - coin)
                
                if 0 <= min_coins_amt_coin < min_coins_amt:
                    min_coins_amt = min_coins_amt_coin + 1
            
            
            count_amount[amount] = -1 if min_coins_amt == float('inf') else min_coins_amt
            
            return count_amount[amount]
        
        if amount <= 0:
            return 0
        count_amount = [float('inf')] * (amount + 1)
        coin_change_for_amount(coins, amount)
        
        return count_amount[amount]
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coinsNeeded = {}
        coinsNeeded[0] = 0
        for n in coins:
            coinsNeeded[n] = 1
        
        
        def findMinCoins(amount):
            # print(amount)
            if amount < 0:
                return float('inf')
            
            if amount in coinsNeeded:
                # coinsNeeded[amount] = min(coinsUsed, coinsNeeded[amount])
                return coinsNeeded[amount]
            
            for n in coins:
                # if amount-n>=0:
                coinsUsed = 1+findMinCoins(amount-n)
                # if coinsUsed > 0:
                if amount in coinsNeeded:
                    coinsNeeded[amount] = min(coinsUsed, coinsNeeded[amount])
                else:
                    coinsNeeded[amount] = coinsUsed


            # if amount in coinsNeeded:
            return coinsNeeded[amount]
            # return -1
        
        findMinCoins(amount)
        # print(coinsNeeded)
        if coinsNeeded[amount] == float('inf'):
            return -1
        # if amount in coinsNeeded:
        return coinsNeeded[amount]
        # return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = {}
        def recursion(S):
            if S == 0:
                return 0
            if S<0:
                return float('inf')
            
            if S in dp:
                return dp[S]
            
            x = float('inf')
            for coin in coins:
                x = min(x, 1 + recursion(S-coin))
            
            dp[S] = x
            return x
        
        res = recursion(amount)
        if res == float('inf'):
            return -1
        
        return res
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # Bottom up approach from video.
        l = [amount+1]*(amount+1)

        for i in range(len(l)):
            if(i==0):
                l[i] = 0
            elif(all(i<c for c in coins)):
                l[i]=-1
            else:
                for j in range(len(coins)):
                    if(i>=coins[j]):
                        num = i-coins[j]
                        if(num>0 and l[num]==-1):
                            if(l[i]!=amount+1):
                                continue
                            else:
                                l[i] = -1
                        else:
                            comb = l[num] + 1
                            if(l[i]==-1):
                                l[i]=amount+1
                            if(l[i]>=comb):
                                l[i] = comb  
                            
        return l[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[float('inf')] * (amount + 1) for _ in range(n + 1)]
        for i in range(n + 1):
            dp[i][0] = 0
        
        for i in range(1, n + 1):
            for j in range(1, amount + 1):
                dp[i][j] = min(dp[i-1][j], (dp[i][j-coins[i-1]] if j >= coins[i-1] else float('inf')) + 1)
        return dp[-1][-1] if dp[-1][-1] < float('inf') else -1
        # @lru_cache(None)
        # def dfs(amt, idx):
        #     # print(amt, idx)
        #     if idx < 0:
        #         if amt == 0:
        #             return 0
        #         else:
        #             return float('inf')
        #     if amt < 0:
        #         return float('inf')
        #     if amt == coins[idx]:
        #         return 1
        #     return min(dfs(amt - coins[idx], idx) + 1, dfs(amt, idx - 1))
        # res = dfs(amount, len(coins)-1)
        # return res if res < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins_sorted = list(sorted(coins, reverse=True))
        memo = {}
        
        def choose(amount):
            if amount in memo:
                return memo[amount]
            if amount < 0:
                return -1
            if amount == 0:
                return 0
            memo[amount] = -1
            for c in coins_sorted:
                if (ret:=choose(amount - c)) >= 0:
                    if memo[amount] != -1:
                        memo[amount] = min(memo[amount], ret+1)
                    else:
                        memo[amount] = ret + 1
            return memo[amount]
        
        return choose(amount)

class Solution:
    IMPOSSIBLE = -1
    
    def computeMinCoins(self, n: int) -> int:
        if n in self.memoization:
            return self.memoization.get(n)
        if n == 0:
            result = 0
        else:
            result = Solution.IMPOSSIBLE
            for coin in self.coins:
                if coin > n:
                    continue
                coins_extra = self.computeMinCoins(n - coin)
                if coins_extra == Solution.IMPOSSIBLE:
                    continue
                plausible_result = 1 + coins_extra
                if result == Solution.IMPOSSIBLE or result > plausible_result:
                    result = plausible_result
        self.memoization[n] = result
        return result
            
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        if amount % coins[0] == 0:
            return amount // coins[0]
        self.coins = coins
        self.memoization = {}
        return self.computeMinCoins(amount)


import math
from functools import lru_cache 

class Solution:

       
    
    ### Iterative DFS
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        
        stack = [(0,0)]  # sum, coinCount
        answer = math.inf
        
        # !! n.b, sorting small->big so stack processing searches largest first
        sortedCoins = sorted(coins) 
        # !! n.b. prune search tree to prevent re-searching same sums
        visited = set()
        
        while stack:
            node = stack.pop()
            (sum, coinCount) = node
            
            if node in visited:
                pass
            elif coinCount >= answer:
                pass
            elif sum == amount:
                answer = min(answer,coinCount)
            elif sum > amount:
                pass
            else:
                for c in sortedCoins:
                    if ( 
                        (sum+c) <= amount and
                        # !! n.b. ensure still possible to beat best answer 
                        # !! with this coin
                        amount < (sum + (c*(answer-coinCount)))
                    ):
                        stack.append((sum+c, coinCount+1))
                        
            visited.add(node)
                
        if answer == math.inf:
            return -1
        else:
            return answer
    
    
    
#     ### Iterative DFS.  Brute force won't finish on large trees
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         stack = [(0,0)]    # (sum, count)
#         # visited = set()
#         answer = math.inf
        
#         while stack:
#             node = stack.pop()
#             # print(node)
            
#             if node[0] == amount:
#                 answer = min(answer, node[1])
#             elif node[0] > amount:
#                 pass
#             else:
#                 for coin in coins:
#                     stack.append( (node[0]+coin, node[1]+1)  )
                    
#         if answer == math.inf:
#             return -1
#         else:
#             return answer
        
        
#     ### Recursive DFS with memoization
#     def coinChange(self, coins: List[int], amount: int) -> int:

#         @lru_cache(maxsize=None)
#         def dfs(remainder):
#             if remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 return 0
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, dfs(remainder - coin) + 1)
#                 return least
        
#         res = dfs(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
    
#     def coinChange(self, coins: List[int], amount: int) -> int:
        
#         def backtrack(remainder):
#             # print(remainder)
#             if remainder in memo:
#                 return memo[remainder]
#             elif remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 memo[remainder] = 0
#                 return memo[remainder]
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, backtrack(remainder - coin) + 1)
#                 memo[remainder] = least
#                 return memo[remainder]    
        
#         memo = collections.defaultdict(int)
#         res = backtrack(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
        
        
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if not coins or amount < 0:
#             return -1
        
#         @lru_cache(maxsize=None)
#         def dfs(sum, count):
#             # print(\"sum:{}, count:{}\".format(sum, count))
#             if count >= self.ans:
#                 return
#             elif sum == amount:
#                 self.ans = min(self.ans, count)
#                 return
#             elif sum > amount:
#                 return
#             else:
#                 for c in sortedCoins:
#                     dfs(sum+c, count+1)
                    
#         sortedCoins = sorted(coins, reverse=True)
#         self.ans = math.inf
#         dfs(0,0)
#         if self.ans == math.inf:
#             return -1
#         else:
#             return self.ans
        
    
#     ### BFS
#     def coinChange(self, coins: List[int], amount: int) -> int:  
#         if not coins or amount < 0:
#             return -1
        
#         visited = [False]*(amount+1)
#         parentNodes = [0]
#         level = -1
        
#         while parentNodes:
#             level += 1
#             childNodes = []
#             for node in parentNodes:
#                 if visited[node]:
#                     pass
#                 elif node == amount:
#                     return level
#                 elif node > amount:
#                     pass
#                 else:
#                     for c in coins:
#                         if node+c <= amount:
#                             childNodes.append(node+c)
#                 visited[node] = True      
                
#             parentNodes = childNodes
            
#         return -1

    
#     ### Dynamic programming
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         maxint = math.inf
        
#         # one slot for each amount, up to amount. 
#         dp = [0] + [maxint]*amount
        
#         for i in range(1,amount+1):
#             dp[i] = min([(1+dp[i-c]) for c in coins if i>=c] + [maxint])
            
#         if dp[amount] == math.inf:
#             return -1
#         else:
#             return dp[amount]
        
    
### Recursive w/memoization    
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         sortedCoins = sorted(coins, reverse=True)
#         result = self.recurse(amount, tuple(sortedCoins))
        
#         if result == math.inf:
#             result = -1
        
#         return result
        
#     @lru_cache(maxsize=None)
#     def recurse(self, amount: int, coins: tuple) -> int:
#         # print(amount)
#         if amount < 0:
#             return math.inf
#         elif amount == 0:
#             return 0
#         else:
#             least = math.inf
#             for c in coins:
#                 val = self.recurse(amount-c, coins)
#                 if val < math.inf:
#                     # print('{} = {}'.format(amount-c, val))
#                     least = min(least, val)
#             return least + 1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        cache = dict()
        def recursiveChange(amount):
            if amount == 0:
                return 0
            if amount < 0:
                return -1
            if amount in cache:
                return cache[amount]
            
            min_val = float('inf')
            for coin_val in coins:
                combination = recursiveChange(amount - coin_val)
                if combination != -1:
                    min_val = min(min_val, combination + 1)
            cache[amount] = min_val
            return min_val
        
        ans = recursiveChange(amount)
        if ans == float('inf'):
            return -1
        return ans

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        use dp 0 - 11
        '''
        if amount == 0:
            return 0
        
        dp = [None] * amount
        dp[0] = 1 if 1 in coins else -1
        
        for i in range(1, amount):
            min_req = float('inf')
            
            for c in coins:
                if (i + 1) % c == 0:
                    min_req = min(min_req, (i + 1) // c)
                elif (i + 1) // c > 0 and dp[i - c] != -1:
                    min_req = min(min_req, dp[i - c] + 1)
            
            if min_req == float('inf'):
                dp[i] = -1
            else:
                dp[i] = min_req
        
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def helper(coins, amount, dp):
            if amount < 0:
                return float('inf')
            if amount == 0:
                return 0
            if amount in dp:
                return dp[amount]
            ans = []
            for i in range(len(coins)):
                use_ci = 1 + helper(coins, amount - coins[i], dp)
                ans.append(use_ci)
                
            dp[amount] = min(ans)
 
            return dp[amount]
        
        if amount <= 0:
            return 0
        dp = {}
        result = helper(coins, amount, dp)
        return -1 if result == float('inf') else result
    



class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        
        # self.coins = coins
        self.coins = sorted(coins, reverse=True)
        
        self.count = {}
        
        return self.helper(amount)
    
    def helper(self, rem):
        # print(rem)
        if rem < 0:
            return -1
        if rem == 0:
            return 0
        
        if rem in self.count:
            return self.count[rem]
        
        min_count = 1000000000
        for coin in self.coins:
            # print(\"coin: {}\".format(coin))
            res = self.helper(rem - coin)
            if res >= 0 and res < min_count:
                min_count = res + 1
            
            if min_count == 1000000000:
                self.count[rem] = -1
            else:
                self.count[rem] = min_count
                
            
        return self.count[rem]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = [0] * (amount+1)
        return self.dfs(coins, amount, memo)
    
    def dfs(self, coins: List[int], amount: int, memo: List[int]) -> int:
        if amount == 0:
            return 0
        
        if amount < 0:
            return -1
        
        if memo[amount] != 0:
            return memo[amount]
        
        minCoins = math.inf
        for coin in coins:
            res = self.dfs(coins, amount - coin, memo) + 1
            
            if res > 0:
                minCoins = min(minCoins, res)
        
        memo[amount] = minCoins if minCoins < math.inf else -1
        
        return minCoins if minCoins < math.inf else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        
        # self.coins = coins
        self.coins = sorted(coins, reverse=True)
        
        self.count = {}
        self.finalcount = 0
        
        return self.helper(amount)
    
    def helper(self, rem):
        # print(rem)
        if rem < 0:
            return -1
        if rem == 0:
            return 0
        
        if rem in self.count:
            return self.count[rem]
        
        min_count = 1000000000
        for coin in self.coins:
            # print(\"coin: {}\".format(coin))
            res = self.helper(rem - coin)
            if res >= 0 and res < min_count:
                min_count = res + 1
            
            if min_count == 1000000000:
                self.count[rem] = -1
            else:
                self.count[rem] = min_count
                
            
        return self.count[rem]


import math
from functools import lru_cache 

class Solution:

       
    
    ### Iterative DFS, with optimizations.  very fast
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        
        stack = [(0,0)]  # sum, coinCount
        answer = math.inf
        
        # !! n.b, sorting small->big so stack processing searches largest first
        sortedCoins = sorted(coins) 
        # !! n.b. prune search tree to prevent re-searching same sums
        visited = set()
        
        while stack:
            (sum, coinCount) = stack.pop()
            # print('sum:{}, coinCount:{}, answer:{}'.format(sum, coinCount, answer))
            
            if (sum, coinCount) in visited:
                pass
            elif coinCount >= answer:
                pass
            elif sum == amount:
                answer = min(answer,coinCount)
            elif sum > amount:
                pass
            else:
                
                
                for c in sortedCoins:
                    if ( 
                        (sum+c) <= amount and
                        # !! n.b. ensure still possible to beat best answer 
                        # !! with this coin
                        amount < (sum + (c*(answer-coinCount)))
                    ):
                        stack.append((sum+c, coinCount+1))
                        
            visited.add((sum, coinCount))
                
        if answer == math.inf:
            return -1
        else:
            return answer
    
    
    
#     ### Iterative DFS.  Brute force won't finish on large trees
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         stack = [(0,0)]    # (sum, count)
#         # visited = set()
#         answer = math.inf
        
#         while stack:
#             node = stack.pop()
#             # print(node)
            
#             if node[0] == amount:
#                 answer = min(answer, node[1])
#             elif node[0] > amount:
#                 pass
#             else:
#                 for coin in coins:
#                     stack.append( (node[0]+coin, node[1]+1)  )
                    
#         if answer == math.inf:
#             return -1
#         else:
#             return answer
        
        
#     ### Recursive DFS with memoization
#     def coinChange(self, coins: List[int], amount: int) -> int:

#         @lru_cache(maxsize=None)
#         def dfs(remainder):
#             if remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 return 0
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, dfs(remainder - coin) + 1)
#                 return least
        
#         res = dfs(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
    
#     def coinChange(self, coins: List[int], amount: int) -> int:
        
#         def backtrack(remainder):
#             # print(remainder)
#             if remainder in memo:
#                 return memo[remainder]
#             elif remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 memo[remainder] = 0
#                 return memo[remainder]
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, backtrack(remainder - coin) + 1)
#                 memo[remainder] = least
#                 return memo[remainder]    
        
#         memo = collections.defaultdict(int)
#         res = backtrack(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
        
        
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if not coins or amount < 0:
#             return -1
        
#         @lru_cache(maxsize=None)
#         def dfs(sum, count):
#             # print(\"sum:{}, count:{}\".format(sum, count))
#             if count >= self.ans:
#                 return
#             elif sum == amount:
#                 self.ans = min(self.ans, count)
#                 return
#             elif sum > amount:
#                 return
#             else:
#                 for c in sortedCoins:
#                     dfs(sum+c, count+1)
                    
#         sortedCoins = sorted(coins, reverse=True)
#         self.ans = math.inf
#         dfs(0,0)
#         if self.ans == math.inf:
#             return -1
#         else:
#             return self.ans
        
    
#     ### BFS
#     def coinChange(self, coins: List[int], amount: int) -> int:  
#         if not coins or amount < 0:
#             return -1
        
#         visited = [False]*(amount+1)
#         parentNodes = [0]
#         level = -1
        
#         while parentNodes:
#             level += 1
#             childNodes = []
#             for node in parentNodes:
#                 if visited[node]:
#                     pass
#                 elif node == amount:
#                     return level
#                 elif node > amount:
#                     pass
#                 else:
#                     for c in coins:
#                         if node+c <= amount:
#                             childNodes.append(node+c)
#                 visited[node] = True      
                
#             parentNodes = childNodes
            
#         return -1

    
#     ### Dynamic programming
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         maxint = math.inf
        
#         # one slot for each amount, up to amount. 
#         dp = [0] + [maxint]*amount
        
#         for i in range(1,amount+1):
#             dp[i] = min([(1+dp[i-c]) for c in coins if i>=c] + [maxint])
            
#         if dp[amount] == math.inf:
#             return -1
#         else:
#             return dp[amount]
        
    
### Recursive w/memoization    
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         sortedCoins = sorted(coins, reverse=True)
#         result = self.recurse(amount, tuple(sortedCoins))
        
#         if result == math.inf:
#             result = -1
        
#         return result
        
#     @lru_cache(maxsize=None)
#     def recurse(self, amount: int, coins: tuple) -> int:
#         # print(amount)
#         if amount < 0:
#             return math.inf
#         elif amount == 0:
#             return 0
#         else:
#             least = math.inf
#             for c in coins:
#                 val = self.recurse(amount-c, coins)
#                 if val < math.inf:
#                     # print('{} = {}'.format(amount-c, val))
#                     least = min(least, val)
#             return least + 1
                


import sys

class Solution:
    def helper(self, coin_sum, coins, memo):
        if coin_sum == 0:
            return 0
        if coin_sum < 0:
            return sys.maxsize

        if memo[coin_sum - 1] != 0:
            return memo[coin_sum - 1]

        res = sys.maxsize
        for c in coins:
            res = min(res, self.helper(coin_sum - c, coins, memo) + 1)

        memo[coin_sum - 1] = res
        return res


    def coinChange(self, coins, amount):
        memo = [0] * amount
        res = self.helper(amount, coins, memo)
        return -1 if res == sys.maxsize else res

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [None for _ in range(amount+1)]
        
        def fewest_num(amount, dp):
            if dp[amount] is not None:
                return dp[amount]
            if amount == 0:
                min_num = 0
            elif amount < min(coins):
                min_num = float('inf')  # use float('inf') as indicator here
            elif amount in set(coins):
                min_num = 1
            else:
                min_num = float('inf')
                for coin in coins:
                    if amount-coin >= 0:
                        num = fewest_num(amount-coin, dp)
                        min_num = min(min_num, num)
                min_num += 1
            
            dp[amount] = min_num
            return min_num
        
        min_num = fewest_num(amount, dp)
        if min_num == float('inf'):
            return -1
        return min_num


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        
        # self.coins = coins
        self.coins = coins
        
        self.count = {}
        
        return self.helper(amount)
    
    def helper(self, rem):
        # print(rem)
        if rem < 0:
            return -1
        if rem == 0:
            return 0
        
        if rem in self.count:
            return self.count[rem]
        
        min_count = 1000000000
        for coin in self.coins:
            # print(\"coin: {}\".format(coin))
            res = self.helper(rem - coin)
            if res >= 0 and res < min_count:
                min_count = res + 1
            
            if min_count == 1000000000:
                self.count[rem] = -1
            else:
                self.count[rem] = min_count
                
            
        return self.count[rem]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
                
        self.memo = {0: 0}
        
        coins.sort()
    
        self.getMinCoins(coins, amount)
        
        if self.memo[amount] == float('inf'):
            
            return(-1)
        
        return(self.memo[amount])
    
    def getMinCoins(self, coins, amount):
        
        
        if amount in self.memo:
            
            return(self.memo[amount])
        
        minCoins = float('inf')
        
        for coin in coins:
            
            if amount - coin < 0:
                
                break
                
            currCoins = self.getMinCoins(coins, amount - coin) + 1
            minCoins = min(minCoins, currCoins)
            
        self.memo[amount] = minCoins
        
        return(self.memo[amount])
        
        
#         numCoins = len(coins)
        
#         # Values in this array equal the number of coins needed to achieve the cost of the index
#         minCoins = [amount + 1] * (amount + 1)
#         minCoins[0] = 0
        
#         # Loop through every needed amount
#         for i in range(amount + 1):
#             # Loop through every coin value
#             for coin in coins:
#                 # Check that the coin is not bigger than the current amount
#                 if coin <= i:
#                     # minCoins[i]: number of coins needed to make amount i
#                     # minCoins[i-coin]: number of coins needed to make the amount before adding 
#                     #                   the current coin to it (+1 to add the current coin)
#                     minCoins[i] = min(minCoins[i], minCoins[i-coin] + 1)
        
#         # Check if any combination of coins was found to create the amount
#         if minCoins[amount] == amount + 1:
#             return -1
        
#         # Return the optimal number of coins to create the amount
#         return minCoins[amount]


class Solution:
    
    def helper(self, amount: int) -> int:
        
        if amount in list(self.cache.keys()):
            #print(amount, self.cache[amount], \"found\")
            return self.cache[amount]
        
        counts = []
        for coin in self.coins:
            if amount - coin > 0: 
                counts.append(self.helper(amount-coin)+1)
            elif amount - coin == 0:
                counts.append(1)
                break
                
        if counts == []:
            self.cache[amount] = sys.maxsize
        else:
            self.cache[amount] = min(counts)
            
        #print(amount, self.cache[amount], \"inserted\")
        return self.cache[amount]
                
            
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coins = coins
        
        if amount == 0:
            return 0
        
        self.cache = {}
        res = self.helper(amount)
        return res if res < 100000000 else -1
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # i = coin index
        # c[i] = coin cost
        # f(0,0) = 0
        # f(coin, amount) = minimum number of coins needed to pay amount
        # f(i, j) = min( f(i-1, j), f(i, j-c[i]) + 1)
        
        dp = []

        for j in range(amount + 1):
            dp.append(amount + 1)

        
        dp[0] = 0
        
        
        for i in range (len(coins)):
            coin = coins[i]
            for j in range(1, amount + 1):
                sameCoin = float('inf')
                if j - coin >= 0:
                    sameCoin = dp[j-coin]
                
                dp[j] = min(sameCoin +1, dp[j])
        
        return dp[j] if dp[j] != amount + 1 else -1

import sys

class Solution:
    def helper(self, coin_sum, coins, memo):
        if coin_sum == 0:
            return 0
        if coin_sum < 0:
            return sys.maxsize

        if memo[coin_sum - 1] != 0:
            return memo[coin_sum - 1]

        res = sys.maxsize
        for c in coins:
            res = min(res, self.helper(coin_sum - c, coins, memo) + 1)

        memo[coin_sum - 1] = res
        return res


    def coinChange(self, coins, amount):
        memo = [0] * amount
        res = self.helper(amount, coins, memo)
        return -1 if res == sys.maxsize else res


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        value=[-1]*(amount+1)
        value[0]=0
        
        for j in range(amount+1):
            for i in range(len(coins)):
                if j>coins[i]:
                    if value[j-coins[i]]!=-1:
                        if value[j]==-1:
                            value[j]=value[j-coins[i]]+1
                        else:
                            value[j]=min(value[j],value[j-coins[i]]+1)
                if j==coins[i]:
                    value[j]=1
        print(value)
        return value[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        d={}
        ans=self.helper(coins,amount,d)
        if ans==100000000:
            return -1
        return ans
    def helper(self,coins,amount,d):
        if amount==0:
            return 0
        ans=100000000
        for i in range(len(coins)-1,-1,-1):
            if coins[i]<=amount:
                if amount-coins[i] in d:
                    sub_ans=d[amount-coins[i]]
                else:
                    sub_ans=1+self.helper(coins,amount-coins[i],d)
                    d[amount-coins[i]]=sub_ans
                ans=min(ans,sub_ans)
        return ans


import math
from functools import lru_cache 

class Solution:

       
    
    ### Iterative DFS
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        
        stack = [(0,0)]  # sum, coinCount
        sortedCoins = sorted(coins, reverse=False)
        answer = math.inf
        visited = set()
        
        while stack:
            # print(sorted(stack, reverse=True))
            (sum, coinCount) = stack.pop()
            
            if (sum, coinCount) in visited:
                pass
            elif coinCount >= answer:
                pass
            elif sum == amount:
                answer = min(answer,coinCount)
            elif sum > amount:
                pass
            else:
                for c in sortedCoins:
                    if ( 
                        (sum+c) <= amount < (sum + (c*(answer-coinCount)))
                    ):
                        stack.append((sum+c, coinCount+1))
                        
            visited.add((sum, coinCount))
                
        if answer == math.inf:
            return -1
        else:
            return answer
    
    
    
#     ### Iterative DFS.  Brute force won't finish on large trees
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         stack = [(0,0)]    # (sum, count)
#         # visited = set()
#         answer = math.inf
        
#         while stack:
#             node = stack.pop()
#             # print(node)
            
#             if node[0] == amount:
#                 answer = min(answer, node[1])
#             elif node[0] > amount:
#                 pass
#             else:
#                 for coin in coins:
#                     stack.append( (node[0]+coin, node[1]+1)  )
                    
#         if answer == math.inf:
#             return -1
#         else:
#             return answer
        
        
#     ### Recursive DFS with memoization
#     def coinChange(self, coins: List[int], amount: int) -> int:

#         @lru_cache(maxsize=None)
#         def dfs(remainder):
#             if remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 return 0
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, dfs(remainder - coin) + 1)
#                 return least
        
#         res = dfs(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
    
#     def coinChange(self, coins: List[int], amount: int) -> int:
        
#         def backtrack(remainder):
#             # print(remainder)
#             if remainder in memo:
#                 return memo[remainder]
#             elif remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 memo[remainder] = 0
#                 return memo[remainder]
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, backtrack(remainder - coin) + 1)
#                 memo[remainder] = least
#                 return memo[remainder]    
        
#         memo = collections.defaultdict(int)
#         res = backtrack(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
        
        
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if not coins or amount < 0:
#             return -1
        
#         @lru_cache(maxsize=None)
#         def dfs(sum, count):
#             # print(\"sum:{}, count:{}\".format(sum, count))
#             if count >= self.ans:
#                 return
#             elif sum == amount:
#                 self.ans = min(self.ans, count)
#                 return
#             elif sum > amount:
#                 return
#             else:
#                 for c in sortedCoins:
#                     dfs(sum+c, count+1)
                    
#         sortedCoins = sorted(coins, reverse=True)
#         self.ans = math.inf
#         dfs(0,0)
#         if self.ans == math.inf:
#             return -1
#         else:
#             return self.ans
        
    
#     ### BFS
#     def coinChange(self, coins: List[int], amount: int) -> int:  
#         if not coins or amount < 0:
#             return -1
        
#         visited = [False]*(amount+1)
#         parentNodes = [0]
#         level = -1
        
#         while parentNodes:
#             level += 1
#             childNodes = []
#             for node in parentNodes:
#                 if visited[node]:
#                     pass
#                 elif node == amount:
#                     return level
#                 elif node > amount:
#                     pass
#                 else:
#                     for c in coins:
#                         if node+c <= amount:
#                             childNodes.append(node+c)
#                 visited[node] = True      
                
#             parentNodes = childNodes
            
#         return -1

    
#     ### Dynamic programming
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         maxint = math.inf
        
#         # one slot for each amount, up to amount. 
#         dp = [0] + [maxint]*amount
        
#         for i in range(1,amount+1):
#             dp[i] = min([(1+dp[i-c]) for c in coins if i>=c] + [maxint])
            
#         if dp[amount] == math.inf:
#             return -1
#         else:
#             return dp[amount]
        
    
### Recursive w/memoization    
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         sortedCoins = sorted(coins, reverse=True)
#         result = self.recurse(amount, tuple(sortedCoins))
        
#         if result == math.inf:
#             result = -1
        
#         return result
        
#     @lru_cache(maxsize=None)
#     def recurse(self, amount: int, coins: tuple) -> int:
#         # print(amount)
#         if amount < 0:
#             return math.inf
#         elif amount == 0:
#             return 0
#         else:
#             least = math.inf
#             for c in coins:
#                 val = self.recurse(amount-c, coins)
#                 if val < math.inf:
#                     # print('{} = {}'.format(amount-c, val))
#                     least = min(least, val)
#             return least + 1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        coins.sort(reverse=True)
        memo = {}
        def dfs(step, rest_amount):
            if rest_amount < 0:
                return -1
            if rest_amount == 0:
                return 0 
            
            if rest_amount in memo:
                return memo[rest_amount]
            min_steps = math.inf
            for coin in coins:
                rest = rest_amount - coin
                #print(c, rest, step)
                res =  dfs(step+1, rest)
                if res >= 0:
                    min_steps = min(min_steps, res)
            
            
            memo[rest_amount] = min_steps + 1 if min_steps != math.inf else -1
            return memo[rest_amount]
                
                
        
        res = dfs(0, amount)
        return res
            


class Solution:   
    def coinChange2(self, coins: List[int], amount: int, count) -> int:
        if amount < 0:
            return -1
        
        if amount == 0:
            return 0
        
        if(count[amount-1] != 0):
            return count[amount-1]
        
        min = 100000000000
        for i, c in enumerate(sorted(coins)[::-1]):
            ret = self.coinChange2(coins, amount-c, count)
            if(ret>=0 and ret<min):
                min = 1+ret
        if(min==100000000000):
            count[amount-1] = -1
        else:
            count[amount-1] = min

        return count[amount-1]
    
    def coinChange(self, coins:List[int], amount: int) -> int:
        if(amount < 1):
            return 0
        return self.coinChange2(coins, amount, [0]*amount)

class Solution(object):
    def __init__(self):
        self.mem = {0: 0}
        
    def getMinCoins(self, coins, amount):
        if amount in self.mem:
            return self.mem[amount]
        minCoins = float('inf')
        for c in coins:
            if amount - c <  0:
                break
            numCoins = self.getMinCoins(coins, amount - c) + 1
            minCoins = min(numCoins, minCoins)
        
        self.mem[amount] = minCoins
        return minCoins
    
    def coinChange(self, coins, amount):
        minCoins = self.getMinCoins(sorted(coins), amount)
        if minCoins == float('inf'):
            return -1
        return minCoins
        


class Solution:
    
    def helper(self, amount: int) -> int:
        
        if amount in list(self.cache.keys()):
            #print(amount, self.cache[amount], \"found\")
            return self.cache[amount]
        
        counts = []
        for coin in self.coins:
            if amount - coin > 0: 
                counts.append(self.helper(amount-coin)+1)
            elif amount - coin == 0:
                counts.append(1)
                break
                
        if counts == []:
            self.cache[amount] = 100000000
        else:
            self.cache[amount] = min(counts)
            
        #print(amount, self.cache[amount], \"inserted\")
        return self.cache[amount]
                
            
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coins = coins
        
        if amount == 0:
            return 0
        
        self.cache = {}
        res = self.helper(amount)
        return res if res < 100000000 else -1
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = dict()

        def recurse(amount, index):
            if (amount == 0):
                return 0

            if (amount in memo):
                return memo[amount]

            minCoins = float('inf')
            
            for coin in coins:
                if (amount - coin >= 0):
                    response = recurse(amount-coin, 0)
                    
                    if (response != -1):
                        minCoins = min(minCoins, response+1)
            
            memo[amount] = minCoins if minCoins != float('inf') else -1
            return memo[amount]
        
        return recurse(amount, 0)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # i = coin index
        # c[i] = coin cost
        # f(0,0) = 0
        # f(coin, amount) = minimum number of coins needed to pay amount
        # f(i, j) = min( f(i-1, j), f(i, j-c[i]) + 1)
        
        dp = []

        for j in range(amount + 1):
            dp.append(float('inf'))

        
        dp[0] = 0
        
        
        for i in range (len(coins)):
            coin = coins[i]
            for j in range(1, amount + 1):
                sameCoin = float('inf')
                if j - coin >= 0:
                    sameCoin = dp[j-coin]
                
                dp[j] = min(sameCoin +1, dp[j])
        
        return dp[j] if dp[j] != float('inf') else -1

class Solution:
      def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        min_coins = float('inf')

        def count_coins(start_coin, coin_count, remaining_amount):
          nonlocal min_coins

          if remaining_amount == 0:
            min_coins = min(min_coins, coin_count)
            return

          # Iterate from largest coins to smallest coins
          for i in range(start_coin, len(coins)):
            remaining_coin_allowance = min_coins - coin_count
            max_amount_possible = coins[i] * remaining_coin_allowance

            if coins[i] <= remaining_amount and remaining_amount < max_amount_possible:
              count_coins(i, coin_count + 1, remaining_amount - coins[i])

        count_coins(0, 0, amount)
        return min_coins if min_coins < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        f = [float('inf') for i in range(amount + 1)]
        
        f[0] = 0
        
        for i in range(1, amount + 1):
            for coin in coins:
                if i - coin >= 0 and f[i - coin] != float('inf'):
                    f[i] = min(f[i], f[i - coin] + 1)
                    
        if f[-1] == float('inf'):
            return -1
        
        return f[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        memo = {}
        def backtrack(memo, curr = amount,):
            if curr == 0:
                return 0
            if memo.get(curr):
                return memo[curr]
            minimum = math.inf
            for coin in coins:
                if curr - coin < 0:
                    continue
                res = backtrack(memo, curr-coin)
                minimum = min(res, minimum)
            minimum = minimum if minimum == math.inf else minimum + 1
            memo[curr] = minimum
            return minimum
        ans = backtrack(memo)
        if ans == math.inf:
            return -1
        return ans




class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        cache = {}
        #reverse the order 
        coins = sorted(coins, reverse = True)
        
#         def recurse(amt, coins, currnumcoins):
   
#             if amt in cache:
#                 return cache[amt]
            
#             cache[amt] =  float('inf')
#             # base case
#             for coin in coins:
#                 if (amt - coin) == 0 :
#                     return currnumcoins + 1
                
#                 elif (amt - coin) > 0:
#                     cache[amt] =  min(recurse(amt-coin, coins, currnumcoins + 1), cache[amt])
            
#             return cache[amt]

        def helper( coins, amount, cache):
            if amount == 0:
                return 0
            elif amount in cache:
                return cache[amount]
            cache[amount] = float('inf')
            for c in coins:
                if amount - c >= 0:
                    cache[amount] = min(cache[amount], helper(coins, amount-c, cache) + 1)
            return cache[amount]
                               
        
        if amount == 0:
            return 0
            
        if min(coins) > amount:
            return -1
        
        # ans = recurse(amount, coins, 0)
        ans = helper(coins, amount, {})
        
        return ans if ans != float('inf') else -1

class Solution:
    
    def helper(self, coins, amount, total, count, mem):
                
        if total > amount:
            return
        
        if total == amount:
            if total not in mem:
                mem[total] = count
            else:
                mem[total] = min(mem[total], count)
            return
        
        for c in coins:
            if total + c not in mem or mem[total+c] > count + 1:
                mem[total+c] = count + 1
                self.helper(coins, amount, total+c, count+1, mem)
                
        return
        
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if not amount:
            return 0
        
        if amount < min(coins):
            return -1
        
        mem = {}
        coins.sort(reverse=True)
        self.helper(coins, amount, 0, 0, mem)
        
        if amount in mem:
            return mem[amount]
        else:
            return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        similar to knapsack problem, except we want minimum coins instead of max items
        
        
        dp: increase coins and amount one at a time
        
           ''   1.   2.   5
        0  0.   0.   0.   0
        1  -1   1    1    1
        2  -1   2    1    1 
        3  -1   3.   2.   2
        4  -1   4.   2.   2
        5  -1   5.   3.   1
        6  -1   6.   3.   2
        7  -1   7.   4.   2
        8  -1   8.   4.   3
        9  -1   9.   5.   3
        10 -1   10.  5.   2
        11 -1   11.  6.   3
        
        fewest_coins[amount][coins_i + 1] = min of (1 + fewest_coins[amount-coins[j]][coins_i]) for j in range(coins_i+1) or -1 if all the fewest_coins[amount-coins[j]][coins_i] are -1
        
        how to effectively start dp array?
        
        '''
        
        fewest_coins = [0 for j in range(amount+1)]
        
        for j in range(1,amount+1):
            fewest_coins[j] = float('inf')
            for coin_i in range(len(coins)):
                if coins[coin_i] == j:
                    fewest_coins[j] = 1
                    break
                elif coins[coin_i] < j:
                    if fewest_coins[j-coins[coin_i]] > 0:
                        fewest_coins[j] = min(fewest_coins[j], fewest_coins[j-coins[coin_i]]+1)
            if fewest_coins[j] == float('inf'):
                fewest_coins[j] = -1
                    
        return fewest_coins[-1]
#         fewest_coins = [0 for j in range(amount+1)]
        
#         for j in range(1,amount+1):
#             fewest_coins[j] = float('inf')
#             for coin_i in range(len(coins)):
#                 if coins[coin_i] == j:
#                     fewest_coins[j] = 1
#                     break
#                 elif coins[coin_i] < j:
#                     if fewest_coins[j-coins[coin_i]] > 0:
#                         fewest_coins[j] = min(fewest_coins[j], fewest_coins[j-coins[coin_i]]+1)
#             if fewest_coins[j] == float('inf'):
#                 fewest_coins[j] = -1
                    
#         return fewest_coins[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        M = [[0 for j in range(amount + 1)] for i in range(len(coins) + 1)]
        
        for j in range(1, amount + 1):
            M[0][j] = 1000000
        
        for i in range(1, len(coins) + 1):
            for j in range(1, amount + 1):
                if j >= coins[i-1]:
                    M[i][j] = min(1 + M[i][j - coins[i-1]], M[i-1][j])
                else:
                    M[i][j] = M[i-1][j]
                    
        return M[-1][-1] if M[-1][-1] < 1000000 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        matrix =[[0 for i in range(amount+1)] for j in range(len(coins)+1)]
        
        for i in range(len(matrix)):
            matrix[i][0] = 0
        for i in range(1,amount+1):
            matrix[0][i] = sys.maxsize
            
        
        
        for i in range(1,len(matrix)):
            for j in range(len(matrix[0])):
                if j<coins[i-1]:
                    matrix[i][j] = matrix[i-1][j]
                else:
                    matrix[i][j] = min(matrix[i-1][j],1+matrix[i][j-coins[i-1]])
        #print (matrix)            
        if matrix[-1][-1] == sys.maxsize:
            return -1
        return matrix[-1][-1]
        
        
        
        


class Solution:
    def __init__(self):
        self.counts = {}
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 0:
            return -1
        if amount == 0:
            return 0
        if amount in self.counts:
            return self.counts[amount]
        minCoins = -1
        for val in coins:
            numCoins = self.coinChange(coins, amount - val)
            if numCoins != -1:
                if minCoins == -1:
                    minCoins = 1 + numCoins
                else:
                    minCoins = min(minCoins, 1+numCoins)
        self.counts[amount] = minCoins
        return minCoins

class Solution:
    def coinChange2(self, coins: List[int], amount: int) -> int:             
        impossible = amount+1;
        cnts = [impossible]*impossible;
        cnts[0] = 0;
        for coin in coins:
            for x in range(coin, impossible):
                cnts[x] = min(cnts[x], cnts[x-coin]+1);
        if cnts[amount] >= impossible:
            return -1;
        return cnts[amount];
    def coinChange(self, coins: List[int], amount: int) -> int:        
        impossible = amount+1;
        self.cnts = [0]*impossible;
        def createCoins(total: int):
            if total == 0:
                return 0;
            if (self.cnts[total]!= 0):
                return self.cnts[total];
            minCnt = impossible;
            for coin in coins:
                if (total-coin < 0):
                    continue;
                cnt = createCoins(total-coin) + 1;
                minCnt = min(cnt, minCnt);
            self.cnts[total] = minCnt;
            return minCnt;
        retV =  createCoins(amount);
        if retV >= impossible:
            return -1
        return retV;

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[[0 for j in range(amount+1)] for i in range(len(coins)+1)]
        
        INT_MAX=sys.maxsize-1
        for i in range(0,amount+1):
            dp[0][i]=INT_MAX
            
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    dp[i][j]=min(1+dp[i][j-coins[i-1]],dp[i-1][j])
                else:
                    dp[i][j]=dp[i-1][j]
                    
        if dp[-1][-1]>=INT_MAX:
            return -1
        return dp[-1][-1]

class Solution:
    def coinChange(self, coin: List[int], amount: int) -> int:
        
        dp = [ [ 9999999 for _ in range(amount+1)] for _ in range(len(coin)+1) ]
        # dp[i][j] will store min coins reqd to reach value j with coins coin[:i+1]
        
        for i in range(1, len(coin)+1):
            dp[i][0] = 0   # don't need any coins to hit balance 0........
            
        for i in range(1, len(coin)+1):
            for j in range(1, amount+1):
                if j>=coin[i-1]:
                    dp[i][j] = min( dp[i][j-coin[i-1]]+1, dp[i-1][j])
                else:
                    dp[i][j] = dp[i-1][j]
        if dp[-1][-1]==9999999:
            return -1
        return dp[-1][-1]        

class Solution:
    # The problem seems to exhibit recursive structure
    # Recursive algorithm outline
    # Base cases
    # - if amount is negative, return -1
    # - if amount is 0, return 0
    # Recursive case
    # - if amount is positive
    # do a recursive call on all elements
    # if no positive elements, return 0
    # else, return 1 + (min of all these positive elements)
    # Complexity: O(n ^ amount/(lowest_element_in_coins)), because its building a tree
    
    def coinChangeDynamic(self, coins: List[int], amount: int, memory_dict: dict) -> int:
        if(amount < 0):
            return -1
        elif amount == 0:
            return 0
        else:
            if(memory_dict.get(amount) is None):
                recursive_call_output = []
                for coin_val in coins:
                    recursive_call_output.append(self.coinChangeDynamic(coins, amount - coin_val, memory_dict))
                min_num_coins = float('inf')
                for num_coins in recursive_call_output:
                    if num_coins >= 0 and num_coins < min_num_coins:
                        min_num_coins = num_coins

                if(min_num_coins == float('inf')):
                    memory_dict[amount] = -1
                    return -1
                else:
                    memory_dict[amount] = min_num_coins + 1
                    return min_num_coins + 1
            else:
                return memory_dict[amount]
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        return self.coinChangeDynamic(coins, amount, {})


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        similar to knapsack problem, except we want minimum coins instead of max items
        
        
        dp: increase coins and amount one at a time
        
           ''   1.   2.   5
        0  0.   0.   0.   0
        1  -1   1    1    1
        2  -1   2    1    1 
        3  -1   3.   2.   2
        4  -1   4.   2.   2
        5  -1   5.   3.   1
        6  -1   6.   3.   2
        7  -1   7.   4.   2
        8  -1   8.   4.   3
        9  -1   9.   5.   3
        10 -1   10.  5.   2
        11 -1   11.  6.   3
        
        fewest_coins[amount][coins_i + 1] = min of (1 + fewest_coins[amount-coins[j]][coins_i]) for j in range(coins_i+1) or -1 if all the fewest_coins[amount-coins[j]][coins_i] are -1
        
        how to effectively start dp array?
        
        '''
        
        # fewest_coins = [[0 for i in range(len(coins)+1)] for j in range(amount+1)]
        fewest_coins = [0 for j in range(amount+1)]
        
#         for i in range(len(coins)+1):
#             fewest_coins[0][i] = 0
        
#         for j in range(1,amount+1):
#             fewest_coins[j][0] = -1

        # for j in range(1,amount+1):
        #     fewest_coins[j][0] = -1
            
        for j in range(1,amount+1):
            fewest_coins[j] = float('inf')
            for coin_i in range(len(coins)):
                if coins[coin_i] == j:
                    fewest_coins[j] = 1
                    break
                elif coins[coin_i] < j:
                    if fewest_coins[j-coins[coin_i]] > 0:
                        fewest_coins[j] = min(fewest_coins[j], fewest_coins[j-coins[coin_i]]+1)
            if fewest_coins[j] == float('inf'):
                fewest_coins[j] = -1
#         for j in range(1,amount+1):
#             for i in range(1,len(coins)+1):
                
#                 fewest_coins[j][i] = float('inf')
#                 for coin_i in range(i):
#                     if coins[coin_i] == j:
#                         fewest_coins[j][i] = 1
#                         break
#                     elif coins[coin_i] < j:
#                         if fewest_coins[j-coins[coin_i]][i] > 0:
#                             fewest_coins[j][i] = min(fewest_coins[j][i], fewest_coins[j-coins[coin_i]][i]+1)
#                 if fewest_coins[j][i] == float('inf'):
#                     fewest_coins[j][i] = -1
                    
        return fewest_coins[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [ [0 for i in range(amount+1)] for i in range(len(coins)) ]
        # coins.sort()
        col = amount+1
        for i in range(len(coins)):
            for j in range(1,col):
                if i == 0:
                    r = i
                    c = j - coins[i]
                    if c < 0 or dp[r][c] == -1:
                        dp[i][j] = -1
                    else:
                        dp[i][j] = dp[r][c] + 1
                else:
                    incIndex = j - coins[i]
                    if incIndex < 0 or dp[i][incIndex] == -1:
                        dp[i][j] = dp[i-1][j]
                    else:
                        dp[i][j] = dp[i][incIndex]+1
                        if dp[i-1][j] != -1:
                            dp[i][j] = min(dp[i-1][j], dp[i][incIndex]+1)
        return dp[len(coins)-1][col-1]

import math
from functools import lru_cache 

class Solution:

       
    
    ### Iterative DFS
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        
        stack = [(0,0)]  # sum, coinCount
        answer = math.inf
        
        # !! n.b, sorting small->big so stack processing searches largest first
        sortedCoins = sorted(coins) 
        # !! n.b. prune search tree to prevent re-searching same sums
        visited = set()
        
        while stack:
            # print(sorted(stack, reverse=True))
            (sum, coinCount) = stack.pop()
            
            if (sum, coinCount) in visited:
                pass
            elif coinCount >= answer:
                pass
            elif sum == amount:
                answer = min(answer,coinCount)
            elif sum > amount:
                pass
            else:
                for c in sortedCoins:
                    if ( 
                        (sum+c) <= amount and
                        # !! n.b. ensure still possible to beat best answer 
                        # !! with this coin
                        amount < (sum + (c*(answer-coinCount)))
                    ):
                        stack.append((sum+c, coinCount+1))
                        
            visited.add((sum, coinCount))
                
        if answer == math.inf:
            return -1
        else:
            return answer
    
    
    
#     ### Iterative DFS.  Brute force won't finish on large trees
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         stack = [(0,0)]    # (sum, count)
#         # visited = set()
#         answer = math.inf
        
#         while stack:
#             node = stack.pop()
#             # print(node)
            
#             if node[0] == amount:
#                 answer = min(answer, node[1])
#             elif node[0] > amount:
#                 pass
#             else:
#                 for coin in coins:
#                     stack.append( (node[0]+coin, node[1]+1)  )
                    
#         if answer == math.inf:
#             return -1
#         else:
#             return answer
        
        
#     ### Recursive DFS with memoization
#     def coinChange(self, coins: List[int], amount: int) -> int:

#         @lru_cache(maxsize=None)
#         def dfs(remainder):
#             if remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 return 0
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, dfs(remainder - coin) + 1)
#                 return least
        
#         res = dfs(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
    
#     def coinChange(self, coins: List[int], amount: int) -> int:
        
#         def backtrack(remainder):
#             # print(remainder)
#             if remainder in memo:
#                 return memo[remainder]
#             elif remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 memo[remainder] = 0
#                 return memo[remainder]
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, backtrack(remainder - coin) + 1)
#                 memo[remainder] = least
#                 return memo[remainder]    
        
#         memo = collections.defaultdict(int)
#         res = backtrack(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
        
        
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if not coins or amount < 0:
#             return -1
        
#         @lru_cache(maxsize=None)
#         def dfs(sum, count):
#             # print(\"sum:{}, count:{}\".format(sum, count))
#             if count >= self.ans:
#                 return
#             elif sum == amount:
#                 self.ans = min(self.ans, count)
#                 return
#             elif sum > amount:
#                 return
#             else:
#                 for c in sortedCoins:
#                     dfs(sum+c, count+1)
                    
#         sortedCoins = sorted(coins, reverse=True)
#         self.ans = math.inf
#         dfs(0,0)
#         if self.ans == math.inf:
#             return -1
#         else:
#             return self.ans
        
    
#     ### BFS
#     def coinChange(self, coins: List[int], amount: int) -> int:  
#         if not coins or amount < 0:
#             return -1
        
#         visited = [False]*(amount+1)
#         parentNodes = [0]
#         level = -1
        
#         while parentNodes:
#             level += 1
#             childNodes = []
#             for node in parentNodes:
#                 if visited[node]:
#                     pass
#                 elif node == amount:
#                     return level
#                 elif node > amount:
#                     pass
#                 else:
#                     for c in coins:
#                         if node+c <= amount:
#                             childNodes.append(node+c)
#                 visited[node] = True      
                
#             parentNodes = childNodes
            
#         return -1

    
#     ### Dynamic programming
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         maxint = math.inf
        
#         # one slot for each amount, up to amount. 
#         dp = [0] + [maxint]*amount
        
#         for i in range(1,amount+1):
#             dp[i] = min([(1+dp[i-c]) for c in coins if i>=c] + [maxint])
            
#         if dp[amount] == math.inf:
#             return -1
#         else:
#             return dp[amount]
        
    
### Recursive w/memoization    
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         sortedCoins = sorted(coins, reverse=True)
#         result = self.recurse(amount, tuple(sortedCoins))
        
#         if result == math.inf:
#             result = -1
        
#         return result
        
#     @lru_cache(maxsize=None)
#     def recurse(self, amount: int, coins: tuple) -> int:
#         # print(amount)
#         if amount < 0:
#             return math.inf
#         elif amount == 0:
#             return 0
#         else:
#             least = math.inf
#             for c in coins:
#                 val = self.recurse(amount-c, coins)
#                 if val < math.inf:
#                     # print('{} = {}'.format(amount-c, val))
#                     least = min(least, val)
#             return least + 1
                


import math
class Solution:
    trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        if amount < 0:
            return -1
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            if required < 0:
                continue
            val = self.trellis[required] if self.trellis[required] != math.inf else self.coinChange(coins, required)
            if val == -1:
                continue
            else:
                minVal = min(minVal, val + 1)
        if minVal == math.inf:
            minVal = -1
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        arr = coins
        w = amount
        
        n = len(arr)
        t = [[0]*(w+1) for _ in range(n+1)]

        for i in range(n+1):
            for j in range(w+1):
                if j == 0 and i!=0:
                    t[i][j] = 0
                if i == 1 and j!=0:
                    t[i][j] = int(j/arr[i-1]) if j%arr[i-1] == 0 else math.inf

        for i in range(2, n+1):
            for j in range(1, w+1):
                if arr[i-1] <= j:
                    t[i][j] = min(t[i][j-arr[i-1]]+1, t[i-1][j])
                else:
                    t[i][j] = t[i-1][j]
        # from pprint import pprint
        # pprint(t)
        return -1 if t[n][w]==math.inf else t[n][w]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def coinChangeHelper(coins, amount, memoisation = {}):
            # memoisation hashmap - key (the amount), value(the minimum coins)
            
            if amount == 0:  # Base case 1
                return 0
            
            if amount < 0:  # Base case 2: This combination of coins was not successfull
                return float('inf')
            
            if amount in memoisation:  # If we have already computed the minimum for this amount before, return cached value 
                return memoisation[amount]
            
            min_coins_used = float('inf')  # Keep track of the minimum coins used
            for i in range(len(coins) - 1, -1, -1): # If the amount is not cached, consider all the coin denominations
                min_coins_used = min(1 + coinChangeHelper(coins, amount - coins[i], memoisation), min_coins_used)
            
            memoisation[amount] = min_coins_used 
            return min_coins_used
            
        result = coinChangeHelper(coins, amount)
        return result if result != float('inf') else -1

        
        
#         def helper(coins, amount):
#             if amount == 0:
#                 return 1
#             if amount < 0:
#                 return float('inf')

#             min_so_far = float('inf')
#             for denomination in reversed(coins):
#                 min_so_far = min(min_so_far, helper(coins, amount - denomination))

#             return min_so_far + 1 if min_so_far != float('inf') else -1
    
#         result = helper(coins, amount)
#         return  result - 1 if result > 0 else -1 


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[0 for _ in range(amount+1)] for _ in range(len(coins)+1)]
        
        for j in range(amount+1):
            dp[0][j] = float('inf')
            
        for i in range(1, len(coins)+1):
            for j in range(1, amount+1):
                if coins[i-1] > j:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = min(1+dp[i][j-coins[i-1]], dp[i-1][j])
        if dp[-1][-1] == float('inf'):
            return -1
        return dp[-1][-1]
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        def dp(m):
            if m in coins: return 1
            if m == 0: return 0
            if m < 0: return float('inf')
            
            if m not in memo:
                ans = float('inf')
                for v in coins:
                    ans = min(1 + dp(m - v), ans)
                memo[m] = ans
            return memo[m]
        return dp(amount) if dp(amount)!= float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
      coinsCount = dict()
      #fill dict:
      for i in range (1, amount+1):
        coinsCount[i] = 9999
      coinsCount[0] = 0
      for i in range(1, amount+1):
        for icoin in range (0, len(coins)):
          curDenom = coins[icoin]
          difference = i - curDenom
          if difference >= 0:
            coinsCount[i] = min(1 + coinsCount[difference], coinsCount[i])
          elif difference == 0:
            coinsCount[i] +=1
      if (coinsCount[amount] != 9999):
        return coinsCount[amount]
      return -1
      


class Solution:
        
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if (amount == 0):
            return 0
        
        self.dp = {}
        min_ = sys.maxsize
        
        for coin in coins:
            if (amount - coin >= 0):
                min_ = min(min_, 1 + self.change(amount - coin, coins))
        
        if (min_ == sys.maxsize):
            return -1 
        
        return min_
        
    
    def change(self, amount, coins):
        if (amount in self.dp):
            return self.dp[amount]
        
        if (amount == 0):
            self.dp[amount] = 0
            return 0
        
        if (amount in coins):
            self.dp[amount] = 1
            return 1
        
        min_ = sys.maxsize
        
        for coin in coins:
            if (amount - coin >= 0):
                min_ = min(min_, 1 + self.change(amount - coin, coins))
        
        self.dp[amount] = min_
        return min_
            
        
    
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        N=[0 for i in range(amount+1)]
        N[0]=0
        for i in range(1,amount+1):
            temp=[i-j for j in coins]
            history=[N[temp2] for temp2 in temp if temp2 <=i and temp2>=0 and N[temp2]!=-1]
            if len(history)==0:
                N[i]=-1
            else:
                N[i]=1+min([N[temp2] for temp2 in temp if temp2 <=i and temp2>=0 and N[temp2]!=-1])
        return N[amount]
                
            
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        similar to knapsack problem, except we want minimum coins instead of max items
        
        
        dp: increase coins and amount one at a time
        
           ''   1.   2.   5
        0  0.   0.   0.   0
        1  -1   1    1    1
        2  -1   2    1    1 
        3  -1   3.   2.   2
        4  -1   4.   2.   2
        5  -1   5.   3.   1
        6  -1   6.   3.   2
        7  -1   7.   4.   2
        8  -1   8.   4.   3
        9  -1   9.   5.   3
        10 -1   10.  5.   2
        11 -1   11.  6.   3
        
        fewest_coins[amount][coins_i + 1] = min of (1 + fewest_coins[amount-coins[j]][coins_i]) for j in range(coins_i+1) or -1 if all the fewest_coins[amount-coins[j]][coins_i] are -1
        
        how to effectively start dp array?
        
        '''
        fewest_coins = [0 for j in range(amount+1)]
        
        for j in range(1,amount+1):
            fewest_coins[j] = float('inf')
            for coin_i in range(len(coins)):
                if coins[coin_i] == j:
                    fewest_coins[j] = 1
                    break
                elif coins[coin_i] < j:
                    if fewest_coins[j-coins[coin_i]] > 0:
                        fewest_coins[j] = min(fewest_coins[j], fewest_coins[j-coins[coin_i]]+1)
            if fewest_coins[j] == float('inf'):
                fewest_coins[j] = -1
                    
        return fewest_coins[-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[0 for i in range(amount+1)] for j in range(len(coins)+1)]
        for j in range(1,amount+1):
            dp[0][j] = float('inf')
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1] <= j:
                    dp[i][j] = min(dp[i-1][j], 1 + dp[i][j-coins[i-1]])
                else:
                    dp[i][j] = dp[i-1][j]
        if dp[len(coins)][amount] == float('inf'):
            return -1
        else:
            return dp[len(coins)][amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def helper(coins, amount, dp):
            if amount < 0:
                return float('inf')
            if amount == 0:
                return 0
            if amount in dp:
                return dp[amount]
            for i in range(len(coins)):
                use_ci = 1 + helper(coins, amount - coins[i], dp)
                if amount not in dp:
                    dp[amount] = use_ci
                else:
                    dp[amount] = min(dp[amount], use_ci)       
            return dp[amount]
        
        if amount <= 0:
            return 0
        dp = {}
        result = helper(coins, amount, dp)
        return -1 if result == float('inf') else result
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # use DP
        N = len(coins)
        M = amount
        table = [[-1]*(N+1) for i in range(M+1)]
        for j in range(N+1):
            table[0][j] = 0
        for m in range(1,M+1):
            for i in range(N):
                c = coins[i]
                if c > m:
                    table[m][i+1] = table[m][i]
                elif table[m-c][i+1] != -1:
                    if table[m][i]!= -1:
                        table[m][i+1] = min(table[m-c][i+1]+1, table[m][i])
                    else:
                        table[m][i+1] = table[m-c][i+1]+1
                else:
                    table[m][i+1] = table[m][i]
        return table[M][N]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        '''
        [0,1,2,3,4,5,6,7,8,9,10]
        [0,1,1,2,2,1,2,2,3,3,2]
        
        '''
        
        dp = [0] + [float('inf') for _ in range(amount)]
        
        for coin in coins:
            for num in range(coin, len(dp)):
                
                dp[num] = min(dp[num-coin] + 1, dp[num])
                
        return dp[amount] if dp[amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        round = 0
        d = set([0])
        while d or round == 0:
            round += 1
            d = set([coin+s for coin in coins for s in d if coin+s<=amount and coin+s not in d])
            if amount in d:
                return round
        return -1
                        
                        
                


from collections import defaultdict

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def coin_change_for_amount(coins, amount):
            nonlocal count_amount
            
            if amount < 0:
                return -1
            
            if amount == 0:
                return 0
            
            if count_amount[amount] != float('inf'):
                return count_amount[amount]
            
            min_coins_amt = float('inf')
            for coin in coins:
                
                min_coins_amt_coin = coin_change_for_amount(coins, amount - coin)
                
                if 0 <= min_coins_amt_coin < min_coins_amt:
                    min_coins_amt = min_coins_amt_coin + 1
            
            
            count_amount[amount] = -1 if min_coins_amt == float('inf') else min_coins_amt
            
            return count_amount[amount]
        
        if amount <= 0:
            return 0
        count_amount = defaultdict(lambda: float('inf'))
        coin_change_for_amount(coins, amount)
        
        return count_amount[amount]
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        if n == 0:
            return 0
        dp = [[amount+1] * (amount+1) for _ in range(n+1)]
        
        for i in range(n+1):
            dp[i][0] = 0
        
        for i in range(1, n+1):
            for j in range(1, amount+1):
                dp[i][j] = dp[i-1][j]
                if j - coins[i-1] >= 0:
                    dp[i][j] = min(dp[i][j], dp[i][j-coins[i-1]]+1)   
        return dp[n][amount] if dp[n][amount] <= amount else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        dp = [-1 for i in range(amount+1)]
        dp[0] = 0
        for i in range(amount):
            mn = math.inf
            pos = False
            for c in coins:
                if i+1>=c and dp[i-c+1]>=0:
            
                    pos = True
                    mn = min(mn, dp[i+1-c]+1)
           
            if pos:
                dp[i+1] = mn
          
      
        return dp[-1]
                    
                    



import sys
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [sys.maxsize for i in range(amount+1)]
        dp[0] = 0
        for i in range(1, amount+1):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i], 1+dp[i-coin])
        return -1 if dp[amount] == sys.maxsize else dp[amount]

class Solution:
    memory = {}
    
    
    def numCoins(self, coins, amount):
        if amount in self.memory:
            return self.memory[amount]
        
        if amount == 0:
            return 0
        
        ncarr = []
        carr = []
        for c in coins:
            if amount >= c:
                nnc = self.numCoins(coins, amount - c)                
                ncarr.append(nnc + 1)
                carr.append(c)
            
        if len(ncarr) > 0:        
            nc = min(ncarr)
            #accum += carr[ncarr.index(nc)]
            self.memory[amount] = nc
            return nc
        else:
            return float('inf')
    
                        
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.memory = {}                
        nc = self.numCoins(coins, amount)
        if nc == float('inf'):
            return -1
        
        return nc
        
            
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[float('inf')] * (amount + 1) for _ in range(n + 1)]
        dp[0][0] = 0
        for i in range(1, n + 1):
            dp[i][0] = 0
            for j in range(1, amount + 1):
                dp[i][j] = min(dp[i - 1][j], (dp[i][j - coins[i - 1]] if j >= coins[i - 1] else float('inf')) + 1)
        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def helper(coins, amount, dp):
            if amount < 0:
                return float('inf')
            if amount == 0:
                return 0
            if amount in dp:
                return dp[amount]
            for i in range(len(coins)):
                use_ci = 1 + helper(coins, amount - coins[i], dp)
                if amount not in dp:
                    dp[amount] = use_ci
                else:
                    dp[amount] = min(dp[amount], use_ci)       
            return dp[amount]
        
        if amount == 0:
            return 0
        if amount<0:
            return -1
        dp = {}
        result = helper(coins, amount, dp)
        return -1 if result == float('inf') else result
    


import math


def rec(coins, amount, used_coins, res):
    if amount == 0:
        res[0] = min(res[0], used_coins)        
    elif coins:
        coin, rest_coins = coins[-1], coins[:-1]
        for i in range(amount // coin, -1, -1):
            if (res[0] - used_coins) * coin >= amount:
                rec(rest_coins, amount - i * coin, used_coins + i, res)

                
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        res = [math.inf]
        rec(sorted(coins), amount, 0, res)
        return res[0] if res[0] != math.inf else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #dp[i] --- for amount i, the fewset coin here
        
        dp = [float('inf') ]* (amount+1)
        dp[0] = 0
        for i in range(1, amount+1):
            for coin in coins:
                if i >= coin:
                    dp[i] = min(dp[i], dp[i-coin] + 1)
        
        if dp[-1] == float('inf'):
            return -1
        
        return dp[-1]

class Solution:
    def helper(self, coins, amount, dp):
        if amount < 0:
            return float('inf')
        if amount == 0:
            return 0
        if amount in dp:
            return dp[amount]
        for i in range(len(coins)):
            use_ci = 1 + self.helper(coins, amount - coins[i], dp)
            if amount not in dp:
                dp[amount] = use_ci
            else:
                dp[amount] = min(dp[amount], use_ci)       
        return dp[amount]
       
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <= 0:
            return 0
        dp = {}
        result = self.helper(coins, amount, dp)
        return -1 if result == float('inf') else result

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[float('inf') for _ in range(amount + 1)] for _ in range(n)]
        
        for r in range(n):
            dp[r][0] = 0

        for a in range(1, amount + 1):
            div, mod = divmod(a, coins[0])
            if mod  == 0:
                dp[0][a] = div

        for i in range(1, n):
            for a in range(1, amount + 1):
                if a - coins[i] >= 0:
                    dp[i][a] = min(dp[i][a - coins[i]] + 1, dp[i - 1][a])
                else:
                    dp[i][a] = dp[i - 1][a]

        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        seen = {0:0}


        def helper(coins, amount):
            
            if amount in seen:
                return seen[amount]
            
            if not coins:
                return -1

            leastCoins = float('Inf')
            for coin in [c for c in coins if c<= amount]:
                next_least = helper(coins, amount-coin)
                if next_least != -1:
                    leastCoins = min(leastCoins, 1+next_least)
            if leastCoins == float('Inf'):
                seen[amount] = -1
                return -1
            else:
                seen[amount] = leastCoins
                return leastCoins
            

        return helper(coins, amount)

import sys
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:        
        
        res = self.helper(sorted(coins, reverse = True), amount, {})
        if res == float('inf'):
            return -1
        return res
    
    def helper(self, coins, amount, dAmounts):
        if amount < 0:
            return float('inf')
        
        if amount == 0:
            return 0
        
        if amount in dAmounts:
            return dAmounts[amount]
        
        for c in coins:
            pathCount = 1 + self.helper(coins, amount-c, dAmounts)
            dAmounts[amount] = min(dAmounts.get(amount, pathCount), pathCount)
        return dAmounts[amount]
            
    '''def helper(self, coins, amount, dp):
        if amount < 0:
            return float('inf')
        if amount == 0:
            return 0
        if amount in dp:
            return dp[amount]
        for i in range(len(coins)):
            use_ci = 1 + self.helper(coins, amount - coins[i], dp)
            if amount not in dp:
                dp[amount] = use_ci
            else:
                dp[amount] = min(dp[amount], use_ci)       
        return dp[amount]
       
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <= 0:
            return 0
        dp = {}
        result = self.helper(coins, amount, dp)
        return -1 if result == float('inf') else result'''


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp(i) = least coins that can sum up to i
        # iterate each coin on i to check if there's less number sums to i
        dp = [float('Inf') for _ in range(amount + 1)]
        dp[0] = 0
        
        for i in range(1, amount+1):
            for coin in coins:
                if i>= coin and dp[i-coin] != float('Inf'):
                    dp[i] = min(dp[i], dp[i-coin] + 1)
                    
        if dp[amount] == float('Inf'): # when the sum cannot be achieved
            return -1
        return dp[amount]

class Solution:
    # Top down DP solution
    def helper(self, coins, amount, dp):
        if amount < 0:
            # cant get a negative amount - need inf coins
            return float('inf')
        if amount == 0:
            # if amount is 0, we need to use 0 coins
            return 0
        if amount in dp:
            # already cached
            return dp[amount]
        for i in range(len(coins)):
            # We use coin i so now amount is amount - coins[i] 
            use_ci = 1 + self.helper(coins, amount - coins[i], dp)
            if amount not in dp:
                dp[amount] = use_ci
            else:
                # either using this coin makes it fewer coins or not
                dp[amount] = min(dp[amount], use_ci)       
        return dp[amount]
        
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <= 0:
            return 0
        dp = {} # dp cache | memoization
        result = self.helper(coins, amount, dp)
        return -1 if result == float('inf') else result
        


INF = 100000000000000000

def hashIntArr(i_arr):
  return '-'.join(list(map(str, i_arr)))

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        mc = [[0 if j == 0 else INF for j in range(amount + 1)] for _ in range(len(coins) + 1)]
        for c in range(1, len(coins) + 1):
          coin_value = coins[c - 1]

          for a in range(1, amount + 1):
            if coin_value <= a:
              mc[c][a] = min(1 + mc[c][a - coin_value], mc[c - 1][a])
            else:
              mc[c][a] = mc[c - 1][a]
              

        min_coins = mc[len(coins)][amount]
        return min_coins if min_coins < INF else -1
      


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n=len(coins)
        t=[[float('inf')-1]* (amount+1) for _ in range(n+1)]
        
        for i in range(n+1):
            t[i][0]=0
        for j in range(amount+1):
            t[0][j]=float('inf')-1
            if j%coins[0]==0:
                t[1][j]=j//coins[0]
            else:
                t[1][j]=float('inf')-1
        
        for i in range(1,n+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    t[i][j]= min(1+t[i][j-coins[i-1]], t[i-1][j])
                else:
                    t[i][j]= t[i-1][j]
        return t[n][amount] if t[n][amount]!=float('inf') else -1


class Solution:
    # Top down DP solution
    def helper(self, coins, amount, dp):
        if amount < 0:
            return float('inf')
        if amount == 0:
            return 0
        if amount in dp:
            return dp[amount]
        for i in range(len(coins)):
            use_ci = 1 + self.helper(coins, amount - coins[i], dp)
            if amount not in dp:
                dp[amount] = use_ci
            else:
                dp[amount] = min(dp[amount], use_ci)       
        return dp[amount]
        
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <= 0:
            return 0
        dp = {}
        result = self.helper(coins, amount, dp)
        return -1 if result == float('inf') else result
        


class Solution:
    
    def __init__(self):
        self.memo = {}
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        ans = self.helper(coins,amount)
        if ans == float('inf'):
            return -1
        return ans
        
    def helper(self, coins, remaining):
        if remaining == 0:
            return 0
        if remaining < 0:
            return float('inf')
        if remaining in self.memo:
            return self.memo[remaining]
        self.memo[remaining] = min([self.helper(coins,remaining-i) for i in coins[::-1]])+1
        return self.memo[remaining]
        
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [ [0 for i in range(amount+1)] for i in range(len(coins)) ]
        # print(dp)
        coins.sort()
        col = amount+1
        for i in range(len(coins)):
            for j in range(1,col):
                if i == 0:
                    r = i
                    c = j - coins[i]
                    # print(r,c)
                    if c < 0 or dp[r][c] == -1:
                        dp[i][j] = -1
                    else:
                        dp[i][j] = dp[r][c] + 1
                else:
                    incIndex = j - coins[i]
                    if incIndex < 0 or dp[i][incIndex] == -1:
                        dp[i][j] = dp[i-1][j]
                    else:
                        dp[i][j] = dp[i][incIndex]+1
                        if dp[i-1][j] != -1:
                            dp[i][j] = min(dp[i-1][j], dp[i][incIndex]+1)
        # print(dp)
        return dp[len(coins)-1][col-1]

class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coins = coins
        self.amount_dict = {0:0}
        for coin in coins:
            self.amount_dict[coin] = 1
        return self.coinChangeHelp(amount)

    @lru_cache
    def coinChangeHelp(self, amount: int) -> int:
        if amount < 0:
            return -1
#         if not amount:
#             return 0
        if amount in self.amount_dict:
            return self.amount_dict[amount]
        max_coin = amount + 1
        for coin in self.coins:
            cur_coin = self.coinChangeHelp(amount - coin)
            if cur_coin >= 0:
                max_coin = min(max_coin, cur_coin+1) 
        max_coin = max_coin if max_coin != (amount+1) else -1
        self.amount_dict[amount]=max_coin
        return max_coin

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0]
        length = len(coins)
        for i in range(1, amount+1):
            dp += [9999]
            for j in range(length):
                if i >= coins[j] and dp[int(i-coins[j])] != 9999:
                    dp[i] = min(dp[i], dp[int(i-coins[j])] + 1)
        if dp[amount] == 9999:
            return -1
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        cl = len(coins)
        dp = [[math.inf for i in range(amount+1)] for j in range(cl)]
        dp[0][0] = 0
        for i in range(1,amount+1):
            if i-coins[0] >= 0:
                dp[0][i] = dp[0][i-coins[0]]+1
        for i in range(1,cl):
            for j in range(amount+1):
                dp[i][j] = dp[i-1][j]
                if (j-coins[i])>= 0:
                    dp[i][j] = min(dp[i][j-coins[i]]+1, dp[i][j])
        if dp[cl-1][amount] == math.inf:
            return -1
        else:
            return dp[cl-1][amount]


import math


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        best_so_far = math.inf
        
        def rec(C, A, used):
            nonlocal best_so_far
            if A == 0:
                best_so_far = min(best_so_far, used)            
            elif C:
                for i in range(A//C[-1], -1, -1):
                    if (best_so_far - used) * C[-1] >= A:
                        rec(C[:-1], A - i*C[-1], used + i)
                    
        rec(coins, amount, 0)
        return best_so_far if best_so_far != math.inf else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        
        self.coins = coins
        
        self.count = {}
        
        return self.helper(amount)
    
    def helper(self, rem):
        # print(rem)
        if rem < 0:
            return -1
        if rem == 0:
            return 0
        
        if (rem-1) in self.count:
            return self.count[rem-1]
        
        min_count = 1000000000
        for coin in self.coins:
            # print(\"coin: {}\".format(coin))
            res = self.helper(rem - coin)
            if res >= 0 and res < min_count:
                min_count = res + 1
            
            if min_count == 1000000000:
                self.count[rem - 1] = -1
            else:
                self.count[rem - 1] = min_count
            
        return self.count[rem-1]


import math
from functools import lru_cache 

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        result = self.recurse(amount, tuple(coins))
        
        if result == math.inf:
            result = -1
        
        return result
        
    @lru_cache(maxsize=None)
    def recurse(self, amount: int, coins: tuple) -> int:
        # print(amount)
        if amount < 0:
            return math.inf
        elif amount == 0:
            return 0
        elif amount in coins:
            return 1
        else:
            least = math.inf
            for c in coins:
                val = self.recurse(amount-c, coins)
                # print('{} = {}'.format(amount-c, val))
                least = min(least, val)
                
            return least + 1
                


class Solution:
    def helper(self, coins, amount, dp):
        if amount < 0:
            return float('inf')
        if amount == 0:
            return 0
        if amount in dp:
            return dp[amount]
        for i in range(len(coins)):
            use_ci = 1 + self.helper(coins, amount - coins[i], dp)
            if amount not in dp:
                dp[amount] = use_ci
            else:
                dp[amount] = min(dp[amount], use_ci)       
        return dp[amount]
        
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <= 0:
            return 0
        dp = {}
        result = self.helper(coins, amount, dp)
        return -1 if result == float('inf') else result
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
            
        def helper(coins, rem, counts):
            if rem < 0: return -1
            if rem == 0: return 0
            if counts[rem-1] != 0: return counts[rem-1]
            min_val = float('inf')
            for coin in coins:
                res = helper(coins, rem - coin, counts)
                if res >= 0 and res < min_val:
                    min_val = res + 1
                if min_val == float('inf'):
                    counts[rem - 1] = -1
                else:
                    counts[rem - 1] = min_val
            return counts[rem-1]
        
        if amount < 1: return 0
        return helper(coins, amount, [0]*amount)

class Solution:
    def coinChange(self, coins: List[int], target: int) -> int:

        table = [[float('inf') for _ in range(target+1)] for _ in range(len(coins)+1)]
    
        for i in range(len(coins)+1):
            table[i][0] = 0
        for i in range(1, len(coins)+1):
            for j in range(target+1):
                if coins[i-1] <= j:
                    a = 1 + table[i][j-coins[i-1]]
                else:
                    a = float('inf')
                b = table[i-1][j]
                if a <= b:
                    table[i][j] = a
                
                else:
                    table[i][j] = b
        return table[-1][-1] if table[-1][-1] != float('inf') else -1

class Solution:
    cache = {}
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.cache = {}
        return self.solve(coins, amount)

    def solve(self, coins, amount):
        if amount == 0:
            return 0
        if amount < 0:
            return -1

        if amount in self.cache:
            return self.cache[amount]

        best = -1
        for c in coins:
            add = self.solve(coins, amount - c)
            if add != -1:
                if best == -1:
                    best = add + 1
                else:
                    best = min(best, add + 1)

        self.cache[amount] = best
        return best

import math
from functools import lru_cache 

class Solution:

       
    
    ### Iterative DFS, with optimizations.  very fast
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins or amount < 0:
            return -1
        
        stack = [(0,0)]  # sum, coinCount
        answer = math.inf
        
        # !! n.b, sorting small->big so stack processing searches largest first
        sortedCoins = sorted(coins) 
        # !! n.b. prune search tree to prevent re-searching same sums
        visited = set()
        
        while stack:
            (sum, coinCount) = stack.pop()
            
            if (sum, coinCount) in visited:
                pass
            elif coinCount >= answer:
                pass
            elif sum == amount:
                answer = min(answer,coinCount)
            elif sum > amount:
                pass
            else:
                
                
                for c in sortedCoins:
                    remainder = (amount - sum) % c
                    
                    if remainder == 0:
                        quotient = (amount - sum) // c
                        answer = min(answer, coinCount + quotient)
                    
                    
                    if ( 
                        (sum+c) <= amount and
                        # !! n.b. ensure still possible to beat best answer 
                        # !! with this coin
                        amount < (sum + (c*(answer-coinCount)))
                    ):
                        stack.append((sum+c, coinCount+1))
                        
            visited.add((sum, coinCount))
                
        if answer == math.inf:
            return -1
        else:
            return answer
    
    
    
#     ### Iterative DFS.  Brute force won't finish on large trees
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         stack = [(0,0)]    # (sum, count)
#         # visited = set()
#         answer = math.inf
        
#         while stack:
#             node = stack.pop()
#             # print(node)
            
#             if node[0] == amount:
#                 answer = min(answer, node[1])
#             elif node[0] > amount:
#                 pass
#             else:
#                 for coin in coins:
#                     stack.append( (node[0]+coin, node[1]+1)  )
                    
#         if answer == math.inf:
#             return -1
#         else:
#             return answer
        
        
#     ### Recursive DFS with memoization
#     def coinChange(self, coins: List[int], amount: int) -> int:

#         @lru_cache(maxsize=None)
#         def dfs(remainder):
#             if remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 return 0
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, dfs(remainder - coin) + 1)
#                 return least
        
#         res = dfs(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
    
#     def coinChange(self, coins: List[int], amount: int) -> int:
        
#         def backtrack(remainder):
#             # print(remainder)
#             if remainder in memo:
#                 return memo[remainder]
#             elif remainder < 0:
#                 return math.inf
#             elif remainder == 0:
#                 memo[remainder] = 0
#                 return memo[remainder]
#             else:
#                 least = math.inf
#                 for coin in coins:
#                     least = min(least, backtrack(remainder - coin) + 1)
#                 memo[remainder] = least
#                 return memo[remainder]    
        
#         memo = collections.defaultdict(int)
#         res = backtrack(amount)
#         if res == math.inf:
#             return -1
#         else:
#             return res
        
        
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if not coins or amount < 0:
#             return -1
        
#         @lru_cache(maxsize=None)
#         def dfs(sum, count):
#             # print(\"sum:{}, count:{}\".format(sum, count))
#             if count >= self.ans:
#                 return
#             elif sum == amount:
#                 self.ans = min(self.ans, count)
#                 return
#             elif sum > amount:
#                 return
#             else:
#                 for c in sortedCoins:
#                     dfs(sum+c, count+1)
                    
#         sortedCoins = sorted(coins, reverse=True)
#         self.ans = math.inf
#         dfs(0,0)
#         if self.ans == math.inf:
#             return -1
#         else:
#             return self.ans
        
    
#     ### BFS
#     def coinChange(self, coins: List[int], amount: int) -> int:  
#         if not coins or amount < 0:
#             return -1
        
#         visited = [False]*(amount+1)
#         parentNodes = [0]
#         level = -1
        
#         while parentNodes:
#             level += 1
#             childNodes = []
#             for node in parentNodes:
#                 if visited[node]:
#                     pass
#                 elif node == amount:
#                     return level
#                 elif node > amount:
#                     pass
#                 else:
#                     for c in coins:
#                         if node+c <= amount:
#                             childNodes.append(node+c)
#                 visited[node] = True      
                
#             parentNodes = childNodes
            
#         return -1

    
#     ### Dynamic programming
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         maxint = math.inf
        
#         # one slot for each amount, up to amount. 
#         dp = [0] + [maxint]*amount
        
#         for i in range(1,amount+1):
#             dp[i] = min([(1+dp[i-c]) for c in coins if i>=c] + [maxint])
            
#         if dp[amount] == math.inf:
#             return -1
#         else:
#             return dp[amount]
        
    
### Recursive w/memoization    
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         sortedCoins = sorted(coins, reverse=True)
#         result = self.recurse(amount, tuple(sortedCoins))
        
#         if result == math.inf:
#             result = -1
        
#         return result
        
#     @lru_cache(maxsize=None)
#     def recurse(self, amount: int, coins: tuple) -> int:
#         # print(amount)
#         if amount < 0:
#             return math.inf
#         elif amount == 0:
#             return 0
#         else:
#             least = math.inf
#             for c in coins:
#                 val = self.recurse(amount-c, coins)
#                 if val < math.inf:
#                     # print('{} = {}'.format(amount-c, val))
#                     least = min(least, val)
#             return least + 1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        d = {}
        for v in coins:
            d[v] = True
        min_coin = coins[-1]
        
        if not coins or not amount:
            return 0
        if len(coins) == 1:
            if amount % coins[0] == 0:
                return int(amount / coins[0])
            else:
                return -1
        
        self.memo = {}
        return self.fewest(coins, amount, d, min_coin)
    
    def fewest(self, coins, amount, d_value, min_coin):
        if amount in self.memo:
            return self.memo[amount]
        
        if amount in d_value:
            return 1
        elif amount < min_coin:
            return -1
        
        _min = float('inf') 
        for v in coins:
            ret = self.fewest(coins, amount - v, d_value, min_coin)
            if ret > 0:
                _min = min(_min, ret + 1)

        _min = -1 if _min == float('inf') else _min
        self.memo[amount] = _min
        return _min


class Solution:
    def coinChange(self, coins, amount):
        n = len(coins)
        dp = [[math.inf for _ in range(amount + 1)] for _ in range(n)]

        for i, coin in enumerate(coins):
            for t in range(amount + 1):
                # * populate the amount=0 columns, as we don't need any coin to make zero amount
                if t == 0:
                    dp[i][t] = 0
                # * if you use elifs, fails
                if i > 0:
                    # * Exclude the coin: In this case, we will take the minimum coin count from the previous set
                    dp[i][t] = dp[i - 1][t]

                if t >= coin:
                    # * include the coin
                    # * dp[i][t-coin] + 1: take the minimum count needed to get the remaining total, plus include ‘1’ for the current coin
                    # ! min(dp[i][t] because we already computed min value up there.
                    dp[i][t] = min(dp[i][t], dp[i][t - coin] + 1)
        # print(dp)
        # amount combinations will be at the bottom-right corner.
        return -1 if dp[n - 1][amount] == math.inf else dp[n - 1][amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[float('inf') for i in range(amount+1)] for i in range(len(coins))]
        
        for amount in range(len(dp[0])):
            if amount % coins[0]  == 0:
                dp[0][amount] = amount // coins[0]
        
        for coin in range(len(dp)):
            dp[coin][0] = 0
            
            
        
        for coin in range(1, len(dp)):
            for amount in range(1, len(dp[0])):
                if amount >= coins[coin]:
                    dp[coin][amount] = min(dp[coin][amount-coins[coin]]+1, dp[coin-1][amount])
                else:
                    dp[coin][amount] = dp[coin-1][amount]
        
        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #dp_table = {0:0}
        dp_table = [float('inf')] * (amount+1)
        #dp_table[0] = 0
        return self.dp(dp_table,coins,amount)
    
    def dp(self,dp_table:[float],coins: List[int], amount: int) ->int:
        if amount == 0:return 0
        if amount < 0 :return -1
        if dp_table[amount] != float('inf'):
            return dp_table[amount]
        # 求最小值：
        res = float('inf')
        for coin in coins: # 每次选择的都是让 amount 减少最多的
            subpb = self.dp(dp_table,coins,amount-coin)
            if subpb == -1:
                continue
            res = min(res,subpb+1)
        dp_table[amount] = res if res != float('inf') else -1
        return dp_table[amount]
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        state = [float('inf')]*amount
        if amount <1:
            return 0
        max_coin = 0
        for c in coins:
            max_coin = max(c,max_coin)
            if c<= amount:
                state[c-1] = 1
        for i in range(amount):
            for c in coins:
                if i-c>=0:
                    state[i] = min(state[i-c]+1,state[i])
        
        return state[-1] if state[-1] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        dp = [0]*(amount+1)
        for item in range(amount+1):
            if (item<coins[0]) and item!=0:
                dp[item] = -1
            elif item == 0:
                dp[item] = 0 
                
        if (amount<coins[0]) and amount == 0:
            return 0
        elif (amount<coins[0]):
            return -1
        
        for i in range(coins[0], amount+1):
            count=0
            
            for j in coins:
                if (i-j)<0:
                    break 
                elif dp[i-j]!=-1:
                    count+=1
            if count>0:
                dp[i] = math.ceil(i/coins[0])
                for j in coins:
                    if (i-j)<0:
                        break 
                    elif dp[i-j] == -1:
                        continue 
                    else:
                        dp[i] = min(dp[i], 1+dp[i-j])
            else:
                dp[i] =-1
        
        return dp[amount]
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0
        
        self.coins = coins
        # self.coins = sorted(coins, reverse=True)
        
        self.count = {}
        
        self.recur_count = 0
        
        ans = self.helper(amount)
        # print(self.recur_count)
        return ans
    
    def helper(self, rem):
        self.recur_count += 1
        # print(rem)
        if rem < 0:
            return -1
        if rem == 0:
            return 0
        
        if rem in self.count:
            return self.count[rem]
        
        min_count = 1000000000
        for coin in self.coins:
            # print(\"coin: {}\".format(coin))
            res = self.helper(rem - coin)
            if res >= 0 and res < min_count:
                min_count = res + 1
            
            if min_count == 1000000000:
                self.count[rem] = -1
            else:
                self.count[rem] = min_count
                
            
        return self.count[rem]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # coins_dict = {coin, coin for coin in coins}
        dp = [-1] * (amount + 1)
        dp[0] = 0
        for i in range(1, amount+1):
            if i in coins:
                dp[i] = 1
            else:
                low = float('inf')
                for coin in coins:
                    if i - coin >= 0 and dp[i - coin] != -1:
                        dp[i] = min(low, 1 + dp[i - coin])
                        low = min(low, 1 + dp[i - coin])
        print(dp)
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #dp_table = {0:0}
        dp_table = [float('inf')] * (amount+1)
        dp_table[0] = 0
        return self.dp(dp_table,coins,amount)
    
    def dp(self,dp_table:[float],coins: List[int], amount: int) ->int:
        if amount == 0:return 0
        if amount < 0 :return -1
        if dp_table[amount] != float('inf'):
            return dp_table[amount]
        # 求最小值：
        res = float('inf')
        for coin in coins:
            subpb = self.dp(dp_table,coins,amount-coin)
            if subpb == -1:
                continue
            res = min(res,subpb+1)
        dp_table[amount] = res if res != float('inf') else -1
        return dp_table[amount]
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[-1]*(amount+1)
        dp[0]=0
        for i in range(1,amount+1):

            for coin in coins[::-1]:
                j=i-coin
#                print('i,j,coin=',i,j,coin)
                if j<0 or dp[j]==-1:
                    continue
                if dp[i]==-1:
                    dp[i]=dp[j]+1
                else:
                    dp[i]=min(dp[i],dp[j]+1)

        
        return(dp[amount])
    
    
    
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = dict()
        def dp(n):
            if n in memo: return memo[n]
            if n == 0: return 0
            if n < 0: return -1
            res = 1000
            for coin in coins:
                if (dp(n-coin) == -1): continue
                res = min(res, 1 + dp(n-coin))
            memo[n] = res if res != 1000 else -1
            return memo[n]
        return dp(amount)

class Solution:
    memory = {}
    
    
    def numCoins(self, coins, amount):
        if amount in self.memory:
            return self.memory[amount]
                        
        nc = float('inf')
        for c in coins:
            if amount >= c:
                nnc = self.numCoins(coins, amount - c)                
                nc = min(nc, nnc+1)           
            
        self.memory[amount] = nc
        return nc        
    
                        
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.memory = {}
        self.memory[0] = 0
        for c in coins:
            self.memory[c] = 1
        
        nc = self.numCoins(coins, amount)
        if nc == float('inf'):
            return -1
        
        return nc
        
            
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        t = [[-1]*(amount+1)]*(len(coins)+1)
        
        for i in range(len(t)):
            for j in range(len(t[0])):
                if j == 0:
                    t[i][j] = 0
                if i == 0:
                    t[i][j] = float('inf')-1
        for j in range(1,amount+1):
            if j % coins[0] == 0:
                t[1][j] = int(j/coins[0])
            else:
                t[1][j] = float('inf') - 1
        for i in range(2,len(t)):
            for j in range(2,len(t[0])):
                if coins[i-1] <= j:
                    t[i][j] = min(1+t[i][j-coins[i-1]], t[i-1][j])
                else:
                    t[i][j] = t[i-1][j]
        if t[-1][-1] != float('inf'):
            return t[-1][-1]
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = {}
        def dfs(amount, path):
            if amount < 0:
                return float('inf')
            if amount == 0:
                return 0
            if amount in memo:
                return memo[amount]
            ans = float('inf')
            for i in coins:
                r = dfs(amount-i, path+1)
                if r != float('inf'):
                    ans = min(ans, r+1)
            memo[amount] = ans
            return ans
        ret = dfs(amount, 0)
        return ret if ret != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        f = [[float('inf')] * (amount + 1) for _ in range(n + 1)]
        f[0][0] = 0
        for i in range(1, n + 1):
            for j in range(amount + 1):
                f[i][j] = f[i - 1][j]
                if coins[i - 1] <= j:
                    f[i][j] = min(f[i][j], 1 + f[i][j - coins[i - 1]])
        return f[n][amount] if f[n][amount] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        @lru_cache(None)
        def dfs(amt, idx):
            # print(amt, idx)
            if idx < 0:
                if amt == 0:
                    return 0
                else:
                    return float('inf')
            if amt < 0:
                return float('inf')
            if amt == coins[idx]:
                return 1
            return min(dfs(amt - coins[idx], idx) + 1, dfs(amt, idx - 1))
        res = dfs(amount, len(coins)-1)
        return res if res < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')]*(amount+1)
        dp[0] = 0
        for i in range(1,amount+1):
            for j in coins:
                if i>=j:
                    dp[i] = min(dp[i],dp[i-j]+1)
        return dp[-1] if dp[-1]!=float('inf') else -1

import math
class Solution:
    trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        if amount < 0:
            return -1
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            if required < 0:
                continue
            val = self.coinChange(coins, required)
            if val == -1:
                continue
            else:
                minVal = min(minVal, val + 1)
        if minVal == math.inf:
            minVal = -1
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse=True)
        d = {}
        for v in coins:
            d[v] = True
        min_coin = min(coins)
        
        if not coins or not amount:
            return 0
        if len(coins) == 1:
            if amount % coins[0] == 0:
                return int(amount / coins[0])
            else:
                return -1
        
        self.memo = {}
        return self.fewest(coins, amount, d, min_coin)
    
    def fewest(self, coins, amount, d_value, min_coin):
        if amount in self.memo:
            return self.memo[amount]
        
        if amount in d_value:
            return 1
        elif amount < min_coin:
            return -1

        _min = float('inf') 
        for v in coins:
            left_amount = amount - v
            if left_amount == 0:
                _min = min(_min, 1)
            elif left_amount > 0:
                ret = self.fewest(coins, left_amount, d_value, min_coin)
                if ret > 0:
                    _min = min(_min, ret + 1)

        _min = -1 if _min == float('inf') else _min
        self.memo[amount] = _min
        return _min


import math
class Solution:
    def __init__(self):
        self.trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            if required < 0:
                continue
            val = self.coinChange(coins, required)
            if val != -1:
                minVal = min(minVal, val + 1)
        minVal = -1 if minVal == math.inf else minVal
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        tracker = [float('inf') for i in range(amount)]
        res = self.coinChange_helper(coins, amount, tracker)
        print(tracker)
        if tracker[-1] == float('inf'):
            return -1
        return res
        
    def coinChange_helper(self, coins:List[int], amount: int, tracking: List[int]) -> int:
        if amount < 0:
            return -1
        
        if amount == 0:
            return 0
        
        if tracking[amount-1] != float('inf'):
            return tracking[amount-1]
        
        lowest = float('inf')
        for i in range(len(coins)):
            result = self.coinChange_helper(coins, amount - coins[i], tracking)
            if result >= 0 and result+1 < lowest: 
                lowest = result + 1
            
        tracking[amount-1] = lowest if lowest != float('inf') else -1
        return tracking[amount-1]
    
        
                    
            
        
        


class Solution:
    
    def do_du_tab(self, coins, amount):
        dp_tab = [math.inf for _ in range(amount + 1)]
        dp_tab[0] = 0
        for i in range(len(coins)):
            for a in range(1, amount + 1):
                if a >= coins[i]:
                    dp_tab[a] = min(dp_tab[a] , 1 + dp_tab[a - coins[i]])
        return dp_tab[amount]

    
    def do_td_mem(self, cache, coins, amount, index):

        if amount == 0:
            return 0

        if len(coins) <= index:
            return math.inf

        if cache[index][amount] == math.inf:
            count_keeping_element = math.inf
            if coins[index] <= amount:
                temp = self.do_td_mem(cache, coins, amount - coins[index], index)
                if temp != math.inf:
                    count_keeping_element = temp + 1

            count_skipping_element = self.do_td_mem(cache, coins, amount, index + 1)
            cache[index][amount] = min(count_keeping_element, count_skipping_element)
        return cache[index][amount]

    
    def do_bf(self, coins, amount, index):
        if amount == 0:
            return 0
        n = len(coins)
        if n <= index:
            return math.inf

        count_keeping_element = math.inf
        if coins[index] <= amount:
            temp = self.do_bf(coins, amount - coins[index], index)
            if temp != math.inf:
                count_keeping_element = temp + 1

        count_skipping_element = self.do_bf(coins, amount, index + 1)
        return min(count_keeping_element, count_skipping_element)
                
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        #output = self.do_bf(coins, amount, 0)
        if amount < 1:
            return 0
        #cache = [[math.inf for _ in range(amount + 1)] for _ in range(len(coins))] 
        #output = self.do_td_mem(cache, sorted(coins), amount, 0)
        coins.sort()
        output = self.do_du_tab(coins, amount)
        return -1 if output == math.inf else output

import math
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # want fewest number of coins to form the amount
        if amount == 0:
            return 0
        records = [-2]*amount
        return helper(amount, coins, records)

def helper(amount, coins, records):
    # base
    if amount == 0:
        return 0
    if amount < 0:
        return -1
    
    # recursive function, iterate, top-down
    if records[amount - 1] != -2:
        min_number = records[amount - 1]
    else:
        min_number = math.inf
        for c in coins:
            val = helper(amount - c, coins, records)
            if val != -1:
                val += 1
                min_number = min(val, min_number)
#                records[amount - 1] = min_number
    if min_number == math.inf:
        records[amount-1] = -1
        return -1
    else:
        records[amount-1] = min_number
        return min_number


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        two choices, take or dont take
        DP[i][j], i is index of coin, j is amount
        
        take:
            DP[i][j] = DP[i][j-coins[i]] + 1
        dont take:
            DP[i][j] = DP[i-1][j]
        
          0 1 2 3 4 5 6 7 8 9 10 11
        1 0 1 2 3 4 5 6 7 8 9 10 11
        2 0 1 1 2
        5    
        '''
        
        dp = [[float('inf')] * (amount+1) for _ in range(len(coins))]
        for r in range(len(coins)):
            dp[r][0] = 0
        
        for r in range(len(coins)):
            for c in range(1, amount+1):
                take = leave = float('inf')
                if c - coins[r] >= 0:
                    take = dp[r][c-coins[r]] + 1
                if r > 0:
                    leave = dp[r-1][c]                    
                dp[r][c] = min(leave, take)
        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1
                


class Solution:
    def coinChange(self, coins: List[int], a: int) -> int:
        l = len(coins)
        mem =[]
        for i in range(l+1):
            mem.append([0]*(a+1))
            for j in range(a+1):
                if i ==0:
                    mem[i][j] =float('inf')
                if j==0:
                    mem[i][j] = 0
                if i>0 and j>0:
                    if coins[i-1]<=j:
                        mem[i][j] = min(1+mem[i][j-coins[i-1]],mem[i-1][j])
                    else:
                        mem[i][j] = mem[i-1][j]
        # /print(len(mem),len(mem[0]))4
        if mem[l][a]==float('inf'):
            return -1
        return  mem[l][a]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        w  = amount
        
        t = [[0 for i in range(w+1)] for j in range(n+1)]
        
        for i in range(n+1):
            for j in range(w+1):
                if i==0:
                    t[i][j] = float('inf')
                if j==0:
                    t[i][j] = 0
                if i==1 and j>0:
                    if j%coins[0]==0:
                        t[i][j] = j//coins[0]
                    else:
                        t[i][j] = float('inf')

                    
              
              
        
        for i in range(2,n+1):
            for j in range(1,w+1):
                if coins[i-1]<=j:
                    t[i][j] = min( t[i-1][j] , 1+t[i][j-coins[i-1]])
                else:
                    t[i][j] = t[i-1][j]
        if t[n][w]== float('inf'):
            return -1
        return t[n][w]
        
        
        


import sys
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[-1 for i in range(amount+1)] for j in range(len(coins)+1)]
        for i in range(len(coins)+1):
            for j in range(amount+1):
                if j == 0:
                    dp[i][j] = 0
                if i == 0:
                    dp[i][j] = sys.maxsize - 1
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1] <= j:
                    dp[i][j] = min(dp[i-1][j] , dp[i][j - coins[i-1]] + 1)
                else:
                    dp[i][j] = dp[i-1][j]
        if dp[-1][-1] == sys.maxsize - 1:
            return -1
        else:
            return dp[-1][-1]

                    
        
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[float('inf')]*(amount+1) for i in range(len(coins))]
        
        for i in range(len(coins)):
            for j in range(amount+1):
                if i == 0:
                    if j % coins[i] == 0:
                        dp[i][j] = j // coins[i]
                    else:
                        dp[i][j] = float('inf')
                elif j - coins[i] < 0:
                    dp[i][j] = dp[i-1][j]
                elif j == coins[i]:
                    dp[i][j] = 1
                elif j > coins[i]:
                    dp[i][j] = min(1 + dp[i][j - coins[i]],dp[i-1][j])
        
        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1

class Solution:
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         n = len(coins)
#         return self.coinChangeHelper(coins, n, amount, {})
        
    
#                #            11
#                #   10       9        6  => 
#                # 9 8 5   8 7 4    5 4 1 => 1
#                #                      0 => 0
#     def coinChangeHelper(self, coins, n, amount, mem):
#         if amount in mem:
#             return mem[amount]
#         if amount == 0:
#             return 0
#         if n == 0:
#             return -1
        
#         minCoins = math.inf
#         for i in range(n):
#             if coins[i] <= amount:
#                 numCoins = self.coinChangeHelper(coins, n, amount - coins[i], mem)
#                 if numCoins != -1:
#                     minCoins = min(minCoins, numCoins)
        
#         if minCoins == math.inf:
#             mem[amount] = -1
#         else:
#             mem[amount] = minCoins + 1
        
#         return mem[amount]
#     [1, 2, 5], amount = 11
    
#      0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27
#  2   0   -1  1   -1  2   -1  3  -1   4   -1  5   -1   6  -1  7   -1  8   -1  9   -1  10  -1  11   
#  5   0   -1  1  -1   2   1   3   2   
# 10
#  1
    
    # if coins[i] <= j: dp[i][j] = min(dp[i][j-coins[i]] + 1, dp[i-1][j])
    # else: dp[i][j] = dp[i-1][j]
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
    
        dp = [[0 for j in range(amount + 1)] for i in range(n)]
        
        for j in range(amount + 1):
            dp[0][j] = -1 if j % coins[0] else j // coins[0]
            
        for i in range(1, n):
            for j in range(1, amount + 1):
                includeDenom = -1 if coins[i] > j else dp[i][j-coins[i]]
                excludeDenom = dp[i-1][j]
                if includeDenom == -1 and excludeDenom == -1:
                    dp[i][j] = -1
                elif includeDenom == -1:
                    dp[i][j] = excludeDenom
                elif excludeDenom == -1:
                    dp[i][j] = includeDenom + 1
                else:
                    dp[i][j] = min(includeDenom + 1, excludeDenom)
                    
        return dp[n-1][amount]
                    


# class Solution:
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         min_coins = float('inf')
#         def backtrack(tot, count):
#             nonlocal min_coins
#             if tot == 0:
#                 min_coins = min(min_coins, count)
#                 return
#             for coin in coins:
#                 if coin <= tot:
#                     backtrack(tot - coin, count + 1)
#         backtrack(amount, 0)
#         return -1 if min_coins == float('inf') else min_coins
    
    
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
#         bottom-up DP
        memo = [[float('inf') for _ in range(amount + 1)] for _ in range(len(coins))]
        for x in range(len(coins)):
            memo[x][0] = 0        
        for i, coin in enumerate(coins):            
            for tot in range(1, amount + 1):
                if i > 0: 
                    memo[i][tot] = memo[i - 1][tot]
                if coin <= tot:
                    memo[i][tot] = min(memo[i][tot], memo[i][tot - coin] + 1)
        out = memo[len(coins) - 1][amount]
        return -1 if out == float('inf') else out

        
#         top-down DP
        # memo = {}
        # def backtrack(tot):            
        #     if tot == 0:
        #         return 0   
        #     if tot not in memo:
        #         min_coins = float('inf')
        #         for coin in coins:
        #             if coin <= tot:
        #                 cur_count = backtrack(tot - coin) + 1
        #                 min_coins = min(min_coins, cur_count)
        #         memo[tot] = min_coins
        #     return memo[tot]
        # out = backtrack(amount)
        # return -1 if out == float('inf') else out


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount==0:
            return 0
        dp={}
        coins=sorted(coins)
        
#         q=deque()
#         q.append(amount)
        
#         while q:
#             node=q.popleft()
#             for coin in coins[::-1]:
#                 diff=node-coin
#                 if diff==0:
#                     return dp[node]+1
#                 elif diff>0:
#                     if diff not in dp:
#                         dp[diff]=1+dp[node]
#                         q.append(diff)

#         return -1
                    
        dp={coin:1 for coin in coins}
        def helper(amount):
            if amount==0:
                return 0
            
            if amount in dp:
                return dp[amount]
            
            dp[amount]=float('inf')
            for i in range(len(coins)-1,-1,-1):
                if amount-coins[i]>=0:
                    dp[amount]=min(dp[amount],1+helper(amount-coins[i]))
                
            return dp[amount]
        
        result=helper(amount)
        if result==float('inf'):
            return -1
        else:
            return result
        
        

                    
                
                
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp={}
        
        coins=sorted(coins)

        def helper(amount):
            if amount==0:
                return 0
            
            if amount in dp:
                return dp[amount]
            
            dp[amount]=float('inf')
            for i in range(len(coins)-1,-1,-1):
                if amount-coins[i]>=0:
                    dp[amount]=min(dp[amount],1+helper(amount-coins[i]))
                
            return dp[amount]
        
        result=helper(amount)
        if result==float('inf'):
            return -1
        else:
            return result
        
        

                    
                
                
        


class Solution:
    # def coinChange(self, coins: List[int], amount: int) -> int:
    #     coins = set(coins)
    #     dp = [float('inf') if i not in coins else 1 for i in range(amount+1)]
    #     dp[0] = 0
    #     for coin in coins:
    #         for i in range(coin, amount+1):
    #             dp[i] = min(dp[i], dp[i-coin]+1)
    #     return -1 if dp[amount] == float('inf') else dp[amount]

    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = set(coins)
        dp = [float('inf') if i not in coins else 1 for i in range(amount+1)]
        result = self.helper(coins, amount,dp) 
        return -1 if result == float('inf') else result
    
    def helper(self,coins, amount, dp):
        if amount < 0:
            return -1
        if amount == 0:
            return 0
        if dp[amount] != float('inf'):
            return dp[amount]
        current_min = float('inf')
        for coin in coins: 
            result = self.helper(coins,amount-coin,dp)
            if result != -1:
                current_min = min(current_min, 1+result)
        if current_min == float('inf'):
            dp[amount] = -1
            return -1
        dp[amount] = current_min
        return current_min
        


import math
class Solution:
    trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            if required < 0:
                continue
            val = self.coinChange(coins, required)
            if val != -1:
                minVal = min(minVal, val + 1)
        if minVal == math.inf:
            minVal = -1
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


import math
class Solution:
    trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            if required < 0:
                continue
            val = self.coinChange(coins, required)
            if val != -1:
                minVal = min(minVal, val + 1)
        minVal = -1 if minVal == math.inf else minVal
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.amount = amount
        self.coins = coins 
        n = len(coins)
        self.memo =[-1]*(self.amount+1)
        val = self.coindp(amount)
        if val == float('inf'):
            return -1
        else:
            return val
        
    def coindp(self, i):
        if i <0: return float('inf')
        if i==0: return 0
        
        if self.memo[i]>=0:
            return self.memo[i]
        
        mini = float('inf')
        for coin in self.coins:
            mini = min(mini, self.coindp(i-coin)+1)
        
        self.memo[i] = mini
        # print(self.memo)
        return self.memo[i]


import math
class Solution:
    trellis = None
    # O(S*n), O(S) for memoisation table
    def coinChange(self, coins: List[int], amount: int) -> int:
        if self.trellis == None:
            self.trellis = [math.inf]*(amount+1)
            self.trellis[0] = 0
        if amount < 0:
            return -1
        if self.trellis[amount] != math.inf:
            return self.trellis[amount]
        
        minVal = math.inf
        for coin in coins:
            required = amount-coin
            val = self.coinChange(coins, required)
            if val == -1:
                continue
            else:
                minVal = min(minVal, val + 1)
        if minVal == math.inf:
            minVal = -1
        self.trellis[amount] = min(self.trellis[amount], minVal)
        return minVal
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def dfs(start, amount, n_coins):
            coin = coins[start]
            div = amount // coin
            amount %= coin
            n_coins += div
            
            if amount == 0:
                self.minimum = min(n_coins, self.minimum)
                return
            
            if start < length:
                dfs(start + 1, amount, n_coins)
                next_coin = coins[start + 1]
                for _ in range(div):
                    amount += coin
                    n_coins -= 1
                    
                    if (self.minimum - n_coins - 1) * next_coin + 1 > amount:
                        dfs(start + 1, amount, n_coins)
                    else:
                        break
        coins.sort(reverse=True)
        self.minimum = float('inf')
        length = len(coins) - 1
        dfs(0, amount, 0)
        return self.minimum if self.minimum < float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount==0:
            return 0
            
        coins.sort(reverse=True)
        res = amount//coins[-1]+1
        def comb(cursum, num, index):
            nonlocal res
            if (amount-cursum)/coins[index] >= res-num:
                return
            
            for i in range(index, len(coins)):
                newsum = cursum + coins[i]
                if newsum==amount:
                    res=min(num+1, res)
                    return              
                elif newsum < amount:
                    comb(newsum, num+1, i)
                    
        comb(0, 0, 0)
        if res==amount//coins[-1]+1:
            return -1
        return res
                    


class Solution:
    def coinChange(self, coin: List[int], sum1: int) -> int:
        maxv=float('inf')-1
        n=len(coin)
        t=[[0 for j in range(sum1+1)] for i in range(n+1)]
        for i in range(n+1):
            for j in range(sum1+1):
                if i==0 and j==0:
                    t[i][j]=maxv
                elif j==0:
                    t[i][j]=0
                elif i==0:
                    t[i][j]=maxv
        for i in range(1,n+1):
            for  j in range(1,sum1+1):
                if coin[i-1]<=j:
                    t[i][j]=min(t[i][j-coin[i-1]]+1,t[i-1][j])
                else:
                    t[i][j]=t[i-1][j]
        if t[n][sum1]==float('inf'):
            return -1
        return t[n][sum1]
      
            
                
        
        


import math

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not coins:
            return -1
        
        if amount == 0:
            return 0
        
        memo = [0] * amount
        def findNumCoins(remainingAmount: int) -> int:
            nonlocal memo

            if remainingAmount < 0:
                # overshot.
                return -1
            if remainingAmount == 0:
                return 0
            memo_idx = remainingAmount - 1
            if memo[memo_idx] != 0:
                return memo[memo_idx]
            
            local_min = math.inf
            for coin in coins:
                res = findNumCoins(remainingAmount - coin)
                if res >= 0:
                    local_min = min(local_min, 1 + res)
            num_coins = -1 if local_min == math.inf else local_min
            memo[memo_idx] = num_coins
            return num_coins
                    
        return findNumCoins(amount)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[0]*(amount+1)
        for i in range(1, amount+1):
            minn=float('inf')
            for j in coins:
                if i==j:
                    minn=min(minn, 1)
                    continue
                if i-j>0:
                    minn=min(minn, 1+dp[i-j])
            dp[i]=minn
        return dp[amount] if dp[amount]!=float('inf') else -1
#         if amount==0:
#             return 0
#         if amount<0:
#             return -1
#         minn=float('inf')
#         for i in coins:
#             count=self.coinChange(coins, amount-i)
#             if count!=-1:
#                 minn=min(count+1, minn)

#         return minn if minn!=float('inf') else -1


class Solution:   
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        
        self.count = [float('inf')]*(amount + 1)
        self.count[0] = 0
        # print(count)
        
        for i in range(1, amount + 1):
            for coin in coins:
                rest = i - coin
                if rest >= 0 and self.count[rest] != float('inf'):
                    self.count[i] = min(1 + self.count[rest], self.count[i])
                    
        if self.count[-1] == float('inf'): 
            return -1
        else:
            return self.count[-1]
    

                
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[math.inf for i in range(amount + 1)] for i in range(len(coins))]
        for i in range(len(coins)):
            dp[i][0] = 0
        for i in range(len(coins)):
            for j in range(amount + 1):
                if i > 0:
                    dp[i][j] = dp[i - 1][j]
                if j >= coins[i]:
                    dp[i][j] = min(dp[i][j], dp[i][j - coins[i]] + 1)
        return -1 if dp[-1][-1] == math.inf else dp[-1][-1]
        
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[float('inf') for i in range(amount+1)] for j in range(len(coins)+1)]
        for i in range(len(coins)+1):
            dp[i][0] = 0

        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]]+1)
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[amount+1 for j in range(n+1)] for i in range(amount+1)]
        for j in range(n+1):
            dp[0][j] = 0
        for i in range(1, amount+1):
            for j in range(1, n+1):
                c = coins[j-1]
                if c > i:
                    dp[i][j] = dp[i][j-1]
                else:
                    dp[i][j] = min(dp[i-c][j]+1, dp[i][j-1])
            
        return dp[amount][n] if dp[amount][n] < amount+1 else -1

from functools import lru_cache

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dict_ = set(coins)
        count = [0]*amount
        # Top down approach
        def top_down(rem):
            if rem < 0:
                return -1
            if rem == 0:
                return 0
            if count[rem - 1] != 0: # cache
                return count[rem - 1];
            
            min_ = float('inf')
            
            for coin in dict_:
                res = top_down(rem - coin)
                if res >= 0:
                    min_ = min(1 + res, min_)
            
            count[rem - 1] = -1 if min_ == float('inf') else min_
            return count[rem - 1]
        
    
        # bottom up approach
        def bottom_up():
            dpa = [float('inf')] * (amount + 1)
            dpa[0] = 0

            for i in range(1, len(dpa)):
                for coin in dict_:
                    if i >= coin:
                        remainder = i - coin
                        dpa[i] = min(dpa[remainder] + 1, dpa[i])
            return dpa[amount] if dpa[amount] != float('inf') else -1 
        
        
        return bottom_up()
        # return top_down(amount)    
        

    
    
        
        
        
                
            
            


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount:
            return 0
        coins.sort()
        self.coins = coins
        self.memo = {}
        self.recursion(amount)
        return -1 if self.memo[amount] == float('inf') else self.memo[amount]
        
    
    def recursion(self, amt):
        min_coins = float('inf')
        if amt == 0:
            return 0
        if amt in self.memo:
            return self.memo[amt]
        for coin in self.coins:
            if coin > amt:
                break
            x = self.recursion(amt - coin)
            min_coins = min(min_coins, x)
        self.memo[amt] = 1 + min_coins
        return self.memo[amt]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount: return 0
        if amount in coins: return 1
        dp = [float('inf')] * (amount+1)
        for coin in coins:
            if coin < amount:
                dp[coin] = 1
        for i in range(1, amount+1):
            for coin in coins:
                if coin < i:
                    dp[i] = min(dp[i], dp[i-coin]+1)
        return dp[-1] if dp[-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        N = len(coins)
        mem = [[None for i in range(amount+1)] for j in range(N+1)]
        
        def helper(target,i):
            if mem[i][target]!=None:
                return mem[i][target]
            if(target==0):
                return 0
            
            if(i==N and target>0):
                return float('inf')
          
            
            if(coins[i]>target):
                mem[i][target] = helper(target,i+1)
            else:
                include = 1+helper(target-coins[i],i)
                exclude = helper(target,i+1)
                mem[i][target] = min(include,exclude)    
            
            return mem[i][target]
        
        ans = (helper(amount,0))
        return ans if ans!=float('inf') else -1 

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        count = collections.defaultdict(int)
        def coinChangeHelper(coins, rem, count):
            if rem < 0:
                return -1
            if rem == 0:
                return 0
            if count[rem] != 0:
                return count[rem]
            minCount = 2**32
            for coin in coins:
                makeChange = coinChangeHelper(coins, rem - coin, count)
                if makeChange > -1 and makeChange < minCount:
                    minCount = makeChange + 1
            
            count[rem] = -1 if minCount == 2 ** 32 else minCount
            return count[rem]
        
        return coinChangeHelper(coins, amount, count)

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [amount+1 for _ in range(0, amount + 1)]
        dp[0] = 0
        for i in range(0, amount + 1):
            for coin in coins:
                if coin <= i:
                    dp[i] = min(dp[i], 1+dp[i-coin])
        if dp[-1] <= amount:
            return dp[-1]
        else:
            return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')]*(amount+1)
        dp[0] = 0
        for c in coins:
            for amt in range(c, amount+1):
                dp[amt] = min(dp[amt], 1 + dp[amt-c])
        
        return dp[amount] if dp[amount] < float('inf') else -1

class Solution:

    def coinChange(self, coins, amount):
        coins.sort()
        stack = [(0, 0, len(coins))] # steps, accumulated
        min_steps = 2**31
        while len(stack) != 0:
            steps, accumulated, sequence = stack.pop()
            if accumulated == amount:
                min_steps = min(min_steps, steps)
            if accumulated > amount or amount - accumulated > coins[sequence-1] * (min_steps-steps):
                continue
            for seq, coin in enumerate(coins[:sequence]):
                stack.append((steps+1, accumulated+coin, seq+1))
        return min_steps if min_steps != 2**31 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0] + [sys.maxsize] * amount
        
        for i in range(1, amount + 1):
            for coin in sorted(coins):
                if i < coin:
                    break
                dp[i] = min(dp[i], dp[i - coin] + 1)
        
        return -1 if dp[-1] == sys.maxsize else dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[0 for j in range(amount+1)] for i in range(len(coins)+1)]
        
        for i in range(len(coins)+1):
            dp[i][0]=0
        for j in range(1,amount+1):
            dp[0][j]=float('inf')-1
        
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    dp[i][j] = min(dp[i-1][j],1+ dp[i][j-coins[i-1]])
                else:
                    dp[i][j] = dp[i-1][j]
        
        return dp[len(coins)][amount] if dp[len(coins)][amount] != float('inf') else -1 
        '''
        def coin_change_2(coins,n,amount):
            if n==0:
                return float('inf')-1
            if amount==0:
                return 0
            if coins[n-1]<=amount:
                return min(coin_change_2(coins,n-1,amount),1+coin_change_2(coins,n,amount-coins[n-1]))
            else:
                return coin_change_2(coins,n-1,amount)
        
        n=len(coins)
        ans = coin_change_2(coins,n,amount)
        return ans if ans  != float('inf') else -1
        '''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [math.inf] * (amount + 1)
        dp[0] = 0
        
        for i in range(1, amount+1):
            for c in coins: 
                if i-c >= 0: 
                    dp[i] = min(dp[i], dp[i-c] + 1)
        return dp[amount] if dp[amount] != math.inf else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [[math.inf for _ in range(amount+1)] for _ in coins]
        
        for i in range(len(coins)):
            dp[i][0] = 0
        
        for i in range(1, amount+1):
            if coins[0] <= i:
                dp[0][i] = 1 + dp[0][i-coins[0]]
                
        for i in range(1, len(coins)):
            for j in range(1, amount+1):
                take, leave = math.inf, math.inf
                if coins[i] <= j:
                    take = 1 + dp[i][j-coins[i]]
                leave = dp[i-1][j]
                dp[i][j] = min(take, leave)
        return dp[-1][-1] if dp[-1][-1] != math.inf else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        if len(coins) == 0:
            return 0
        coins = sorted(coins, reverse=True)
        memo = {}

        def coinChangeRec(index, amount):
            if amount == 0:
                return 0
            if amount < 0 or index == len(coins):
                return math.inf
            if (index, amount) in memo:
                return memo[(index, amount)]
            
            withCoin = coinChangeRec(index, amount - coins[index]) + 1
            withoutCoin = coinChangeRec(index+1, amount)
            
            memo[(index, amount)] = min(withCoin, withoutCoin)
            return min(withCoin, withoutCoin)
                     
        minCoins = coinChangeRec(0, amount)
        if minCoins == math.inf:
            return -1
        return minCoins
    


class Solution:
    def aux(self, coins, amount, cache):
        if amount in cache: 
            return cache[amount]

        
        res = -1
        for i in range(len(coins)):
            if coins[i] < amount:
                right = cache[amount-coins[i]] if amount-coins[i] in cache else self.aux(coins, amount-coins[i], cache)
                
                if right!=-1 and res==-1:
                    res = 1+right
                elif right!=-1 and res!=-1:
                    res = min(res, 1+right)
                    
                
        
        cache[amount] = res
        return cache[amount]
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount==0: return 0
        cache = dict(list(zip(coins, [1]*len(coins))))
        return self.aux(coins, amount, cache)
        
        
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i] means how many coins we need to make up i money
        # formula: dp[i] = min(dp[i - k] + 1 for k in coins)
        # start point: dp[i] = 1 if i in coins
        # we can add a 1 offset here since dp[0] doesn't make any sense
        if not amount:
            return 0
        dp = [-1 for _ in range(amount)]
        coins = set(coins)
        for i in range(amount):
            if (i + 1) in coins:
                dp[i] = 1
                continue
            for coin in coins:
                if (i - coin) >= 0 and dp[i - coin] != -1:
                    if dp[i] == -1:
                        dp[i] = dp[i - coin] + 1
                    else:
                        dp[i] = min(dp[i], dp[i - coin] + 1)   
        return dp[-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:

        if len(coins) == 0:
            return 0
        coins = sorted(coins, reverse=True)
        memo = {}

        def coinChangeRec(index, amount):
            if amount == 0:
                return 0
            if amount < 0 or index == len(coins):
                return math.inf
            if (index, amount) in memo:
                return memo[(index, amount)]
            
            withCoin = coinChangeRec(index, amount - coins[index]) + 1
            withoutCoin = coinChangeRec(index+1, amount)
            
            memo[(index, amount)] = min(withCoin, withoutCoin)
            return min(withCoin, withoutCoin)
                     
        minCoins = coinChangeRec(0, amount)
        if minCoins == math.inf:
            return -1
        return minCoins
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dic = {0:0}
        
        def change(amount):
            if amount < 0: return -1
            if amount in dic: return dic[amount]
            res = [change(amount - i) for i in coins if change(amount - i) >= 0]
            if not res:
                dic[amount] = -1
            else:
                dic[amount] = min(res) + 1
            return dic[amount]
        
        return change(amount)

class Solution:
    def __init__(self):
        self.table = {}

    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 0:
            return -1
        if amount == 0:
            return 0
        if amount in self.table:
            return self.table[amount]
        possible_answers = [self.coinChange(coins, amount - coin) + 1 for coin in coins]
        possible_answers = [ans for ans in possible_answers if ans >= 1]
        if possible_answers:
            ans = min(possible_answers)
        else:
            ans = -1
        self.table[amount] = ans
        return ans

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # dp[i][j] represents minimum coins needed to change i amount using upto jth coins
        # dp[i][j] = min(dp[i][j-1], dp[i-coins[j]][j])
        
        sz = len(coins)+1
        dp = [[sys.maxsize] * sz for _ in range(amount+1)]
        
        for i in range(sz):
            dp[0][i] = 0
    
        for a in range(1, amount+1):
            for i in range(1, sz):
                dp[a][i] = dp[a][i-1]
                if a-coins[i-1] >= 0:
                    dp[a][i] = min(1 + dp[a-coins[i-1]][i], dp[a][i])
        
  
        return -1 if dp[amount][sz-1] == sys.maxsize else dp[amount][sz-1]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if not amount or not coins:
            return 0
        
        dp = [[math.inf for _ in range(amount + 1)] for _ in range(len(coins))]
        
        for i in range(len(coins)):
            dp[i][0] = 0
            
        for i in range(1, amount + 1):
            if coins[0] <= i:
                dp[0][i] = 1 + dp[0][i-coins[0]]
        for i in range(1, len(coins)):
            for j in range(1, amount + 1):
                take, leave = math.inf, math.inf
                if coins[i] <= j:
                    take = 1 + dp[i][j-coins[i]]
                leave = dp[i-1][j]
                dp[i][j] = min(take, leave)
        return dp[-1][-1] if dp[-1][-1] != math.inf else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = dict()
        def dp(n):
            if n in memo:
                return memo[n]
            if n == 0:
                return 0
            if n < 0:
                return -1
            res = float('inf')
            for coin in coins:
                sub = dp(n - coin)
                if sub == -1:
                    continue
                res = min(res, dp(n - coin) + 1)
            memo[n] = res if res != float('inf') else -1
            return memo[n]
        return dp(amount)

class Solution:
    def coinChange(self, coin: List[int], amount: int) -> int:
        
        dp = [ [ 9999999 for _ in range(amount+1)] for _ in range(len(coin)+1) ]
        # dp[i][j] will store min coins reqd to reach value j with coins coin[:i+1]
        
        for i in range(1, len(coin)+1):
            dp[i][0] = 0   # don't need any coins to hit balance 0........
            
        for i in range(1, len(coin)+1):
            for j in range(1, amount+1):
                if j>=coin[i-1]:
                    dp[i][j] = min( dp[i][j-coin[i-1]]+1, dp[i-1][j], dp[i-1][j-coin[i-1]]+1)
                else:
                    dp[i][j] = dp[i-1][j]
                
        if dp[-1][-1]==9999999:
            return -1
        return dp[-1][-1]        

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        dp = [amount]*(amount+1)
        dp[0] = 0
        pos = 1
        contain = False
        while pos < len(dp):
            for coin in coins:
                if coin == 1:
                    contain = True
                if pos - coin >= 0:
                    dp[pos] = min(dp[pos],dp[pos-coin]+1)
            pos+=1
        print(dp)
        if dp[-1] == amount and not contain:
            return -1
        return dp[-1]
            
        
        


class Solution:
    def coinChange(self, coins: List[int], a: int) -> int:
        if(a==0):
            return 0
        dp=[-1 for i in range(a+1)] 
        dp[0]=1
        # for i in range(a+1):
            # dp[0][i]=10**9
        # for i in range(len(coins)+1):
            # dp[i][0]=10**9
        coins.sort()
        for i in range(1,len(coins)+1):
            for j in range(1,a+1):
                if(coins[i-1]==j):
                    dp[j]=1
                else:
                        c=-1
                        if(j-coins[i-1]>0 and dp[j-coins[i-1]]!=-1):
                            c=dp[j-coins[i-1]]+1
                        d=dp[j]
                        if(c!=-1 and d!=-1):
                            dp[j]=min(c,d)
                        elif(c==-1 and d!=-1):
                            dp[j]=d
                        elif(c!=-1):
                            dp[j]=c
                        
        # print(dp) 
        if(dp[a]==-1):
            return -1
        else:
            # ans=0
            return dp[a]
            
            
                    
                


class Solution:
    def coinChange(self,coins,amount):
        if amount < 1:
            return 0

        def count_change(coins,amount,dic={}):
            if amount < 0:
                return -1
            if amount == 0:
                return 0
            if amount in dic:
                return dic[amount]
            minimum = float('inf')

            for i in range(len(coins)):
                current = count_change(coins, amount-coins[i],dic)
                if current >=0 and current < minimum:
                    minimum = 1 + current
            if minimum == float('inf'):
                dic[amount] = -1
            else:
                dic[amount] = minimum
            return dic[amount]
        
        return count_change(coins,amount,{})

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        dp = [[float('inf') for _ in range(amount+1)] for _ in range(len(coins)+1)]

        for c in range(1, len(dp)):
            dp[c][0] = 0
            for amt in range(1, len(dp[0])):
                if amt - coins[c-1] >= 0:
                        dp[c][amt] = min(1 + dp[c][amt-coins[c-1]], dp[c-1][amt])
                else:
                    dp[c][amt] = dp[c-1][amt]

        return dp[-1][-1] if dp[-1][-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[amount+1 for j in range(n+1)] for i in range(amount+1)]
        for j in range(n+1):
            dp[0][j] = 0
        for i in range(1, amount+1):
            for j in range(1, n+1):
                if coins[j-1] > i:
                    dp[i][j] = dp[i][j-1]
                else:
                    dp[i][j] = min(dp[i-coins[j-1]][j]+1, dp[i][j-1])
            
        return dp[amount][n] if dp[amount][n] < amount+1 else -1

class Solution:
    def __init__(self):
        self.count = []
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        if len(self.count) < amount:
            self.count = [-1]* (amount+1)
            self.skip = [False]* (amount+1)
            
        if amount is 0:
            return 0
        elif amount < 0:
            return -1
        else:
            if self.count[amount] < 0 and not self.skip[amount]:
                tmp = float('inf')
                for co in coins:
                    previous = self.coinChange(coins, amount - co)
                    if previous >= 0:
                        self.count[amount] = min(previous + 1, tmp)
                        tmp = self.count[amount]
                    else:
                        continue
                self.skip[amount] = True
                
            return self.count[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # coins.sort()
        # min_coins = [math.inf]*(amount+1)
        # min_coins[0] = 0
        # for coin in coins:
        #     for amount in range(coin,amount+1):
        #         min_coins[amount] = min(min_coins[amount], min_coins[amount-coin]+1)
        # # print(min_coins)
        # if min_coins[-1] == math.inf:
        #     return -1
        # else: return min_coins[-1]
        cache = {}
        def subproblem(i,t):
            if t == 0:
                return 0
            if (i,t) in cache:
                return cache[(i,t)]
            val = coins[i]
            if val > t:
                choice_take = math.inf
            elif val == t:
                choice_take = 1
            else:
                choice_take = 1 + subproblem(i,t-val)
                
            if i == 0:
                choice_leave = math.inf
            else:
                choice_leave = subproblem(i-1,t)
            optimal = min(choice_take, choice_leave)
            cache[(i,t)] = optimal
            return optimal
        
        mincoins = subproblem(len(coins)-1, amount)
        if mincoins == math.inf:
            return -1
        else:
            return mincoins


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        t=[[0 for x in range(amount+1)] for  x in range(len(coins)+1)]
        for j in range(amount+1):
            t[0][j]=1000
            
        for i in range(1,len(coins)+1):
            t[i][0]=0
            
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if j%coins[i-1]==0:
                    t[i][j]=j/coins[i-1]
                else:
                    t[i][j]=1000
                    
        for i in range(1,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    t[i][j]=min(t[i][j-coins[i-1]]+1,t[i-1][j])
                else:
                    t[i][j]=t[i-1][j]
        if t[len(coins)][amount]!=1000:
            return t[len(coins)][amount]
        return -1
            
                
    
                    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.res = float('inf')
        n = len(coins)
        coins.sort(reverse=True)
        
        def helper(cur, start, cnt, n):
            if cur==amount: self.res = min(self.res, cnt)
            if cur>amount: return 
            
            for i in range(start, n):
                if cur+coins[i]<=amount and cur+coins[i]*(self.res-cnt)>amount:
                    helper(cur+coins[i], i, cnt+1, n)   
            
        helper(0, 0, 0, n)
        return self.res if self.res<float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1)
        dp[0] = 0
        
        for i in range(1,amount+1):
            for coin in coins:
                if (i >= coin):
                    dp[i] = min(dp[i], dp[i - coin] + 1)        
        return dp[amount] if dp[amount] != float('inf') else -1 

# Went through Approach 2 and 3
# Similar to Word Break 


class Solution:
    def coinChange(self, coins: List[int], target: int) -> int:

        table = [[float('inf') for _ in range(target+1)] for _ in range(len(coins)+1)]
    
        for i in range(len(coins)+1):
            table[i][0] = 0
        for i in range(1, len(coins)+1):
            for j in range(target+1):
                if coins[i-1] <= j:
                    a = 1 + table[i][j-coins[i-1]]
                else:
                    a = float('inf')
                b = table[i-1][j]
                table[i][j] = min(a, b)

        return table[-1][-1] if table[-1][-1] != float('inf') else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #d(m,n) indicates using upto the first m kinds of coins, the number of fewest coins needed to make up to n
        coins.sort()
        n = len(coins)
        
        d = [[-1] * (amount + 1) for i in range(n + 1)]
        
        for i in range(amount + 1):
            if i % coins[0] == 0:
                d[1][i] = i // coins[0]
        for i in range(n + 1):
            d[i][0] = 0
        for i in range(2, n + 1):
            for j in range(1, amount + 1):
                if j - coins[i - 1] >= 0:
                    if d[i - 1][j] != -1 and d[i][j - coins[i - 1]] != -1:
                        d[i][j] = min(d[i - 1][j], d[i][j - coins[i - 1]] + 1)
                    elif d[i - 1][j] == -1 and d[i][j - coins[i - 1]] != -1:
                        d[i][j] = d[i][j - coins[i - 1]] + 1
                    elif d[i - 1][j] != -1 and d[i][j - coins[i - 1]] == -1:
                        d[i][j] = d[i - 1][j]
                    else:
                        d[i][j] = d[i - 1][j]
                else:
                    d[i][j] = d[i - 1][j]

        return d[n][amount]
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #coin exchange
        # coins = [3,5,6]
        # amount = 9
        
        coins.sort()
        n_coins = len(coins)
        least_coin = coins[0]
        max_amount = amount + 1

        mat = [[0 for i in range(amount+1)] for i in range(n_coins+1)]

        if amount == 0:
            return 0
        
        if amount < least_coin:
            return (-1)

        for i in range(amount+1):
            if i % least_coin == 0:
                mat[1][i] = i // least_coin
            else:
                mat[1][i] = -1

        for i in range(2,n_coins+1):
            for j in range(1,amount+1):                
                curr_coin = coins[i-1]
                if j - curr_coin >= 0:
                    if mat[i][j - curr_coin] == -1:
                        mat[i][j] = mat[i-1][j]
                    elif mat[i-1][j] == -1:
                        mat[i][j] = 1 + mat[i][j - curr_coin]
                    elif mat[i][j - curr_coin] == -1 and mat[i-1][j] == -1:
                        mat[i][j] = -1
                    else:
                        mat[i][j] = min(1 + mat[i][j - curr_coin], mat[i-1][j])
                else:
                    mat[i][j] = mat[i-1][j]
        # print('-'*20)
        # for row in mat:
        #     print(row)
        return mat[-1][-1]



class Solution:
    def back_tracking_solution(self):
        def _is_solution(amount_left):
            if amount_left == 0:
                return True
            return False
        
        def _process_solution(num_coins_used, fewest_coins):
            if num_coins_used < fewest_coins[0]:
                fewest_coins[0] = num_coins_used
        
        def _candidate_coins_start_idx(amount_left, coins, current_coin_start_idx):                 
            for i in range(current_coin_start_idx, len(coins)):
                if amount_left >= coins[i]:
                    return i
                
            return None
        
        def _backtrack(amount_left, num_coins_used, current_fewest_coins, coins, current_coin_start_idx):
            if _is_solution(amount_left):
                _process_solution(num_coins_used, current_fewest_coins)
            else:
                real_coin_start_idx = _candidate_coins_start_idx(amount_left, coins, current_coin_start_idx)
                if real_coin_start_idx is not None:
                    for i in range(real_coin_start_idx, len(coins)):
                        _backtrack(amount_left - coins[i], num_coins_used + 1, current_fewest_coins, coins, i)
        
        return _backtrack
    
    def dynamic_programming_solution(self):
        def _dp_bad(amount, coins):
            sorted_coins = sorted(coins)
            cache = [[float('inf')] * len(coins) for i in range(amount + 1)]
            
            for denom_idx in range(len(sorted_coins)):
                cache[0][denom_idx] = 0
            
            for amt in range(amount + 1):
                cache[amt][0] = (amt // sorted_coins[0]) if ((amt % sorted_coins[0]) == 0) else float('inf')
            
            #print(f'first: {cache}')
            for i in range(1, len(cache)):
                for j in range(1, len(sorted_coins)):
                    max_num_of_denom = (i // sorted_coins[j]) + 1
                    for k in range(max_num_of_denom):
                        #print(f'BEFORE: i: {i}, j:{j}, k: {k}, each: {cache}')
                        cache[i][j] = min(cache[i][j], k + cache[i - (k*sorted_coins[j])][j-1])
                        #print(f'AFTER: i: {i}, j:{j}, k: {k}, each: {cache}')
                        
            
            #for idx, change in enumerate(cache):
                #print(f'amount: {idx}, change: {change}')
            
            return cache[amount][-1]
        
        def _dp_cache_recursive(amount, coins, cache):
            if amount in cache:
                return cache[amount]
            
            for c in coins:
                if (amount >= c):
                    intermediate = 1 + _dp_cache_recursive(amount - c, coins, cache)
                    if amount in cache:
                        cache[amount] = min(cache[amount], intermediate)
                    else:
                        cache[amount] = intermediate
            
            return cache[amount] if (amount in cache) else float('inf')
    
        def _dp_bottom_up_1(amount, coins):
            sorted_coins = sorted(coins)
            
            cache = [[float('inf')] * len(coins) for i in range(amount + 1)]
            
            for denom_idx in range(len(sorted_coins)):
                cache[0][denom_idx] = 0
            
            for amt in range(1, len(cache)):
                for denom_idx in range(len(sorted_coins)):
                    amt_left = amt - sorted_coins[denom_idx]
                    
                    if (amt_left >= 0):
                        cache[amt][denom_idx] = 1 + min(cache[amt_left][0:(denom_idx + 1)])
            
            #for amt, num in enumerate(cache):
                #print(f'amount: {amt}, num: {num}')
            
            return min(cache[amount])
        
        return _dp_bottom_up_1
        
        
    def coinChange(self, coins: List[int], amount: int) -> int:
        # backtracking solution
        # fewest_coins = [float('inf')]
        # coins_sorted_desc = sorted(coins, reverse = True)
        # self.back_tracking_solution()(amount, 0, fewest_coins, coins_sorted_desc, 0)
        # return -1 if fewest_coins[0] == float('inf') else fewest_coins[0]
    
        # Dynamic Programming - recursive call result caching
        # result = self.dynamic_programming_solution()(amount, coins, cache = {0: 0})
        # return -1 if (result == float('inf')) else result
        
        # Dynamic Programming - non-recursive bottom-up version 1
        result = self.dynamic_programming_solution()(amount, coins)
        return -1 if (result == float('inf')) else result

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:

        coins = sorted(coins, reverse=True)
        memo = {}

        def coinChangeRec(index, amount):
            if amount == 0:
                return 0
            if amount < 0 or index == len(coins):
                return math.inf
            if (index, amount) in memo:
                return memo[(index, amount)]
            
            withCoin = coinChangeRec(index, amount - coins[index]) + 1
            withoutCoin = coinChangeRec(index+1, amount)
            
            memo[(index, amount)] = min(withCoin, withoutCoin)
            return min(withCoin, withoutCoin)
                     
        minCoins = coinChangeRec(0, amount)
        if minCoins == math.inf:
            return -1
        return minCoins
    


import queue


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        
        q = queue.Queue()
        q.put(amount)
        q.put(None)
        level = 0
        visited = {amount}
        while not q.empty():
            num = q.get()
            if num is None:
                if q.empty():
                    break
                q.put(None)
                level += 1
                continue
            for coin in coins:
                if num - coin > 0 and (num - coin) not in visited:
                    q.put(num - coin)
                    visited.add(num - coin)
                elif num - coin == 0:
                    return level+1
        
        return -1
        
        
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        lenc, self.res = len(coins), 2**31-1

        def dfs(pt, rem, count):
            if not rem:
                self.res = min(self.res, count)
            for i in range(pt, lenc):
                if coins[i] <= rem < coins[i] * (self.res-count): # if hope still exists
                    dfs(i, rem-coins[i], count+1)

        for i in range(lenc):
            dfs(i, amount, 0)
        return self.res if self.res < 2**31-1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        m = Counter(coins)

        def recurse(left, count) -> int:
            if left in m:
                return m[left]
            
            if left == 0:
                m[left] = count
                return count
            
            smallest = math.inf
            for c in coins:
                if left - c >= 0:
                    smallest = min(smallest, recurse(left-c, count+1))
                
            m[left] = smallest+1
            return smallest+1

        recurse(amount,0)

        if m[amount] == math.inf:
            return -1
        return m[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        table = [float('inf') for _ in range(amount + 1)]
        i = 0
        table[0] = 0
        for coin in coins:
            if coin <= amount:
                table[coin] = 1
        while i <= amount:
            for coin in coins:
                if  i - coin >= 0:
                    table[i] = min(table[i - coin] + 1, table[i])
            i += 1
        return table[amount] if table[amount] != float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [0]*(amount + 1)
        for i in range(1, len(dp)):
            small = float('inf')
            for j in range(len(coins) - 1, -1, -1):
                if (i - coins[j] >= 0):
                    small = min(small, 1 + dp[i - coins[j]])
            dp[i] = small
        return dp[-1] if dp[-1] != float('inf') else -1

import math

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        self.c = set([x for x in coins if x<=amount])
        self.coins = list(self.c)
        self.dic = {}
        if amount == 0: return 0
        if amount in self.c: return 1
        res = math.inf
        for i in range(len(self.coins)):
            ci = self.dp(amount-self.coins[i])
            if ci >0:
                res = min(res, ci+1)
        return res if res != math.inf else -1

    def dp(self, amount):
        if amount in self.c:
            return 1
        if amount in self.dic:
            return self.dic[amount]
        list2 = [x for x in self.coins if x<=amount]
        if len(list2) == 0:
            return -1
        res = math.inf
        for i in range(len(list2)):
            ci = self.dp(amount-list2[i])
            if ci >0:
                res = min(res, ci+1)
        self.dic[amount] = res
        return res

class Solution:
    def __init__(self):
        self.cache = dict()

    # some key takeaways again:
    # need to reverse sort because of the problem - trying to find min, so need to cache for min
    # because if get a cache hit then it won't search for more, even if it's not min
    
    # the dfs structure here helps because doing a min over a for loop
    def helper(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        coins = sorted(coins,reverse=True)
        if amount in self.cache:
            return self.cache[amount]
        if amount in coins:
            return 1
        potentials = [float('inf')]
        for coin in coins:
            if amount > coin:
                potentials.append(self.helper(coins,amount - coin))
        res = 1 + min(potentials)
        self.cache[amount] = res
        return res

    def coinChange(self, coins: List[int], amount: int) -> int:
        res = self.helper(coins,amount)
        if res == float('inf'):
            return -1
        return res
        


class Solution:
    def helper(self, coins, amount, cache, current = []):
        if amount == 0:
            return 0
        if amount < 0:
            return -1
        
        else:
            if amount in cache:
                return cache[amount]
            
            currMin = sys.maxsize 
            
            for coin in coins:
                current.append(coin) # choose that coin
                currNum = self.helper(coins, amount - coin, cache, current)
                
                if currNum != -1:
                    if currNum < currMin:
                        currMin = currNum + 1
                
                current.pop()
            
            cache[amount] = currMin
            return currMin
            
            
    def coinChange(self, coins, amount):
        cache = {}
        val = self.helper(coins, amount, cache)
        
        # if val == sys.maxsize:
        #     return -1
        # else:
        #     return val
        
        # for each slot in memoization array, subtract each coin from it
        # find min and add 1
        
        # F(0) is 0
        memo = [sys.maxsize] * (amount + 1)
        memo[0] = 0
        
        for i in range(1, amount + 1):
            for coin in coins:
                newIndex = i - coin
                
                if newIndex >= 0:
                    if memo[newIndex] + 1 < memo[i]:
                        memo[i] = memo[newIndex] + 1

        if memo[amount] == sys.maxsize:
            return -1
        else:
            return memo[amount]


class Solution:
    def coinChange(self, coins: [int], amount: int) -> int:
        self.ans = float('inf')
        self.dict = {}  # amount : [depth]
        coins.sort(reverse=True)

        def helper(num, depth):

            if num == 0:
                self.ans = min(self.ans, depth)
                return

            for c in coins:
                res = num - c
                if res >= 0:
                    if res in self.dict:
                        if self.dict[res] > depth + 1 and depth + 1 < self.ans:
                            self.dict[res] = depth+1
                            helper(res, depth + 1)
                    else:
                        self.dict[res] = depth + 1
                        helper(res, depth + 1)

        helper(amount, 0)
        return self.ans if self.ans < float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        @lru_cache(None)
        def recurse(amount,pos):
            if pos==len(coins):
                return 10000
            if amount ==0:
                return 0
            
            
            if coins[pos]<=amount:
                return min(recurse(amount-coins[pos],pos)+1,
                          recurse(amount,pos+1))
            else:
                return recurse(amount,pos+1)
            
            
        count = recurse(amount,0)
        if count>=10000:
            return -1
        return count
            
            


class Solution:
    def recursion(self, coins, remain, dic):
        if(remain < 0):
            return float('inf')
        if(remain == 0):
            return 0
        if(remain in dic.keys()):
            return dic[remain]
        min_coin = float('inf')
        for coin in coins:
            number_coin = None
            prev_num  = self.recursion(coins, remain - coin, dic)
            if(prev_num == float('inf')):
                number_coin = prev_num
            else:
                number_coin = prev_num + 1
            min_coin = min(min_coin, number_coin)
        dic[remain] = min_coin
        return min_coin
    def coinChange(self, coins: List[int], amount: int) -> int:
        remain = amount
        count = 0
        dic = {}
        number = self.recursion(coins, remain, dic)
        if(number == float('inf')):
            return -1
        else:
            return number

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def helper(coins, amount, n, t):
            if n == 0:
                return float('inf')
            if amount == 0:
                return 0
            if n == 1 and amount % coins[n - 1] == 0:
                return amount // coins[n - 1]
            if t[n][amount] == -1:
                if coins[n - 1] <= amount:
                    t[n][amount] = min(1 + helper(coins, amount - coins[n - 1], n, t), helper(coins, amount, n - 1, t))
                else:
                    t[n][amount] = helper(coins, amount, n - 1, t)
            return t[n][amount]
        
        n = len(coins)
        t = [[-1 for j in range(amount + 1)] for i in range(n + 1)]
        minCoins = helper(coins, amount, n, t)
        if minCoins == float('inf'):
            return -1
        else:
            return minCoins

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = {}
        
        # print(coins)
        # print(amount)
                
        
        
        def dfs(total):
            if total < 0:
                return float('inf')
            
            if total == 0:
                return 0
            
            if total in list(dp.keys()):
                return dp[total]
            
            for c in coins:
                x = total - c
                if total not in list(dp.keys()):
                    dp[total] = dfs(x) + 1
                else:
                    dp[total] = min(dp[total], dfs(x) + 1)
                    
            return dp[total]
            

            
        ans = dfs(amount)

        if ans < float('inf'):
            return ans
        else:
            return -1
                
                
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        coins.sort()
        table = [[-1 for i in range(amount + 1)] for i in range(len(coins))]

        for i in range(len(coins)):
            for j in range(amount + 1):
                if coins[i] > j and i > 0 and table[i-1][j] > 0:
                    table[i][j] = table[i-1][j]
                elif j >= coins[i]:
                    x = j - coins[i]
                    pre_val = 9999999
                    calculated_val = 0
                    if i > 0 and table[i - 1][j] > 0:
                        pre_val = table[i - 1][j]
                        
                    if x > 0:
                        if table[i][x] > 0:
                            calculated_val = table[i][x]
                            table[i][j] = min(1 + calculated_val, pre_val)
                        else:
                            if i > 0 and pre_val != 9999999:
                                table[i][j] = pre_val
                    elif x == 0:
                        table[i][j] = min(1 + calculated_val, pre_val)
        return table[len(coins)-1][amount]
                
                


class Solution:
# min(cnt(11-1),cnt(11-2),cnt(11-5))     
    def coinChange(self, coins: List[int], total: int) -> int:
        dic = {0:0,}
        
        def helper(amount):
            
            if amount in coins:
                dic[amount] = 1
            
            elif amount<0:
                return float('inf')
            
            elif amount not in dic:
                dic[amount] = min([helper(amount-c)+1 for c in coins])
            
            return dic[amount]
        
        return helper(total) if helper(total)!=float('inf') else -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coin_count = len(coins)
        
        dp = [[math.inf for i in range(coin_count + 1)] for j in range(amount + 1)]
        for i in range(coin_count + 1):
            dp[0][i] = 0 
        for i in range(1, amount + 1):
            for j in range(1, coin_count + 1):
                if coins[j - 1] <= i:
                    dp[i][j] = min(1 + dp[i - coins[j - 1]][j], dp[i][j - 1])
                else:
                    dp[i][j] = dp[i][j - 1]
        if dp[-1][-1] == math.inf: return -1
        return dp[-1][-1]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if not coins:
            return -1
        
        n=len(coins)
        
        if amount==0:
            return 0
        
        fewest=[[0]*n for k in range(amount+1)]
        
        for k in range(n):
            fewest[0][k]=0
            
        for p in range(1,amount+1):
            is_divisible=(p%coins[0])==0
            
            if is_divisible:
                fewest[p][0]=(p//coins[0])
                
            else:
                fewest[p][0]=-1
        
        
        for k  in range(1,n):
            for p in range(1,amount+1):
                if p<coins[k]:
                    fewest[p][k]=fewest[p][k-1]
                    
                elif fewest[p-coins[k]][k]==-1:
                    fewest[p][k]=fewest[p][k-1]
                
                elif fewest[p][k-1]==-1:
                    fewest[p][k]=fewest[p-coins[k]][k]+1
                else:
                    fewest[p][k]=min(fewest[p-coins[k]][k]+1,fewest[p][k-1])
                    
                
             
        return fewest[p][n-1]
                    
                    
            


class Solution:
#     def coinChange_bottomUp(self, coins: List[int], amount: int) -> int:
#         # Intialize the lookup, and initialize a base case of 0
#         lookup = {x: amount + 1 for x in range(1, amount + 1)}
#         lookup[0] = 0

#         for i in range(amount + 1):
#             for coin in coins:
#                 remainder = i - coin
#                 if remainder < 0:
#                     continue
#                 best_min = min(lookup[remainder] + 1, lookup[i])
#                 lookup[i] = best_min
#         if lookup[i] > amount:
#             return -1
#         else: 
#             return lookup[i]
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        coins.reverse()
        lookup = {}
        
        def find_combos(total_remain, total_coins):
            
            if total_remain in lookup:
                if lookup[total_remain] > total_coins:
                    lookup[total_remain] = total_coins
                else:
                    return
            else:
                lookup[total_remain] = total_coins
            
            for coin in coins:
                if total_remain - coin < 0:
                    continue
                find_combos(total_remain - coin, total_coins + 1)
        
        find_combos(amount, 0)
        
        if 0 in lookup:
            return lookup[0]
        else:
            return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        dp = {i : []  for i in range(1,amount+1)}
        coins.sort()
        for intermediate_amount in range(1,amount+1):
            '''
                basic algorithm: 
                    - for each number, start from the end of the coins list
                    - subtract the given denomination
                    - check if the hash map has an entry for the amount - denomination
                    - use the first possible configuration
                    - if all possibilities are exhausted, no denomination exists
            '''
            for denom in reversed(coins):
                if intermediate_amount == denom: 
                    dp[intermediate_amount] = [denom]
                    break # we know this is the minimum number of denoms, we can stop here
                elif denom < intermediate_amount and dp[intermediate_amount - denom] != []:
                    # update the array if it's the first time OR if len(dp[intermediate_amount]) > len(dp[intermediate_amount - denom]) + 1
                    if dp[intermediate_amount] == [] or len(dp[intermediate_amount]) > len(dp[intermediate_amount - denom]) + 1:
                        dp[intermediate_amount] = dp[intermediate_amount - denom] + [denom]
        return len(dp[amount]) if len(dp[amount]) > 0 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
       
        dp = [[float('INF') for i in range(amount + 1)] for j in range(len(coins)+1)]
        for i in range(len(coins)+1):
            dp[i][0] = 0
        for i in range( 1,len(coins)+1):
            for j in range(1, amount+1):
                if j - coins[i-1]< 0:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = min( dp[i-1][j], dp[i][j-coins[i-1]] + 1 )

        if dp[len(coins)][amount] == float('INF'):
            return -1
        else:
            return dp[len(coins)][amount]


'''
find the minimum number of coins that you need to make up the amt -> DP

recursive
amt 
c1, c2, c3 
count = float('inf')
base case:
    if amt<0:
       return -1
    if amt == 0:
        return 0
for coin in coins:
  #use that coin - take the coin or do not take the coin
  count = min(count,1+coinchangehelper(amt-coin))


recursion with memoization

'''


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        def coinchangehelper(rem):
            if rem<0:
                return -1
            if rem==0:
                return 0
            if rem in memo:
                return memo[rem]
            count = float('inf')
            for coin in coins:
                sub = coinchangehelper(rem-coin)
                if sub>=0:
                    count = min(count,1+sub)
            memo[rem] = count
            return memo[rem]
        memo = {}
        res = coinchangehelper(amount)
        return res if res!=float('inf') else -1   
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf') for _ in range(amount+1)]
        
        if(amount == 0):
            return 0

        for i in range(0, len(dp)):
            for c in coins:
                if(i >= c):
                    if(i % c == 0):
                        dp[i] = min(int(i / c), dp[i])
                    else:
                        if(i-c >= 0 and dp[i-c] != float('inf')):
                            dp[i] = min(int(1 + dp[i - c]), dp[i])
        
        ret_val = dp[amount]
        if(ret_val == float('inf')):
            return -1
        return dp[amount]

import sys
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp=[[0 for i in range(amount+1)]for j in range(len(coins)+1)]
        for i in range(len(coins)+1):
            for j in range(amount+1):
                if i==0:
                    dp[i][j]=sys.maxsize
        for i in range(len(coins)+1):
            for j in range(amount+1):
                if j%coins[0]==0:
                    dp[i][j]=j//coins[0]
                else:
                    dp[i][j]=sys.maxsize
        for i in range(2,len(coins)+1):
            for j in range(1,amount+1):
                if coins[i-1]<=j:
                    dp[i][j]=min(dp[i][j-coins[i-1]]+1,dp[i-1][j])
                else:
                    dp[i][j]=dp[i-1][j]
        if dp[len(coins)][amount]==sys.maxsize:
            return -1
        return dp[len(coins)][amount]


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
    # (3) Dynamic Programming - top-down
        self.dp = [float('inf')] * (amount + 1)
        self.dp[0] = 0
        
        def change(a):
            if a < 0:
                return -1
            if a == 0:
                return 0
            if self.dp[a] != float('inf'):
                return self.dp[a]
            for c in coins:
                tmp = change(a-c)
                if tmp != -1:
                    self.dp[a] = min(tmp + 1, self.dp[a])
            if self.dp[a] == float('inf'):
                self.dp[a] = -1
            # remember to return
            return self.dp[a]
        
        change(amount)
        return self.dp[amount] if self.dp[amount] != float('inf') else -1

class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:
        self.coins = coins
        self.amount_dict = {0:0}
        # for coin in coins:
        #     self.amount_dict[coin] = 1
        return self.coinChangeHelp(amount)

    @lru_cache
    def coinChangeHelp(self, amount: int) -> int:
        if amount in self.amount_dict or amount < 0:
            return self.amount_dict.get(amount, -1)
        max_coin = amount + 1
        for coin in self.coins:
            cur_coin = self.coinChangeHelp(amount - coin)
            if cur_coin >= 0:
                max_coin = min(max_coin, cur_coin+1) 
        max_coin = max_coin if max_coin != (amount+1) else -1
        self.amount_dict[amount]=max_coin
        return max_coin

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        def dfs(start, amount, n_coins):
            coin = coins[start]
            div = amount // coin
            n_coins += div
            amount %= coin
            
            if amount == 0:
                self.minimum = min(n_coins, self.minimum)
                return
            
            if start < length:
                dfs(start + 1, amount, n_coins)
                next_coin = coins[start + 1]
                for _ in range(div):
                    amount += coin
                    n_coins -= 1
                    if (self.minimum - n_coins - 1) * next_coin + 1 > amount:
                        dfs(start + 1, amount, n_coins)
                    else:
                        break
        coins.sort(reverse=True)
        self.minimum = float('inf')
        length = len(coins) - 1
        dfs(0, amount, 0)
        return self.minimum if self.minimum < float('inf') else -1

class Solution:
    def coinChange_bottomUp(self, coins: List[int], amount: int) -> int:
        # Intialize the lookup, and initialize a base case of 0
        lookup = {x: amount + 1 for x in range(1, amount + 1)}
        lookup[0] = 0

        for i in range(amount + 1):
            for coin in coins:
                remainder = i - coin
                if remainder < 0:
                    continue
                best_min = min(lookup[remainder] + 1, lookup[i])
                lookup[i] = best_min
        if lookup[i] > amount:
            return -1
        else: 
            return lookup[i]
    
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        coins.reverse()
        lookup = {}
        
        def find_combos(total_remain, total_coins):
            
            if total_remain in lookup:
                if lookup[total_remain] > total_coins:
                    lookup[total_remain] = total_coins
                else:
                    return
            else:
                lookup[total_remain] = total_coins
            
            for coin in coins:
                if total_remain - coin < 0:
                    continue
                find_combos(total_remain - coin, total_coins + 1)
        
        find_combos(amount, 0)        
        return lookup[0] if 0 in lookup else -1


# BSF No.1
# class Solution:
#     def coinChange(self, coins: List[int], amount: int) -> int:
#         if amount == 0:
#             return 0
#         queue = [[0, 0]]
#         visited = {0}
#         step = 0
        
#         for node, step in queue:
#             for coin in coins:
#                 if node + coin in visited:
#                     continue
#                 if node + coin == amount:
#                     return step + 1
#                 if node + coin < amount:
#                     queue.append([node + coin, step + 1])
#                     visited.add(node + coin)
#         return -1
    
# BSF No.2
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        level = seen = {0}
        number = 0
        while level:
            # print(level)
            if amount in seen:
                return number
            level = {pre_amount + coin for pre_amount in level for coin in coins if pre_amount + coin <= amount}
            seen.update(level)
            # print(seen)
            number += 1
        return -1
    # level = seen = {0}
    # number = 0
    # while level:
    #     if amount in level:
    #         return number
    #     level = {a+c for a in level for c in coins if a+c <= amount} - seen
    #     seen |= level
    #     number += 1
    # return -1 


from functools import lru_cache
from math import inf
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        @lru_cache(maxsize=None)
        def cc(i, amount):
            if amount == 0:
                return 0
            elif i < 0 or amount < 0:
                return inf
            else:
                incl, excl = cc(i-1, amount), cc(i, amount-coins[i]) + 1
                return min(max(incl, 0), max(excl, 0))
        
        
        result = cc(len(coins)-1, amount)
        return result if result < inf else -1
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        memo = [ [ -1 for _ in range(amount+1) ] for _ in coins ]
        
        for i in range(len(coins)-1, -1, -1):
            for j in range(amount+1):
                if j == 0:
                    memo[i][j] = 0
                    continue
                    
                    
                if j-coins[i] >= 0 and memo[i][j-coins[i]] != -1 and i+1 < len(coins) and memo[i+1][j] != -1:
                    memo[i][j] = min(memo[i][j-coins[i]]+1, memo[i+1][j])
                elif j-coins[i] >= 0 and memo[i][j-coins[i]] != -1:
                    memo[i][j] = memo[i][j-coins[i]]+1
                elif i+1 < len(coins) and memo[i+1][j] != -1:
                    memo[i][j] = memo[i+1][j]
        return memo[0][amount]
        


class Solution:
    def __init__(self):
        self.total_min = 0
        self.change_map = {0:0}
        
    def changer(self, coins_obj, amount_obj):
        
        if amount_obj == 0:
            self.change_map[amount_obj] = 0
            return 0
        if min(coins_obj) > amount_obj:
            self.change_map[amount_obj] = -1
            return -1
            
        rev_coins_obj = list(reversed(coins_obj))
        for el in rev_coins_obj:
            if el <= amount_obj:
                if amount_obj-el in self.change_map.keys():
                    tmp = self.change_map[amount_obj-el]
                else:
                    tmp = Solution.changer(self,coins_obj,amount_obj-el)
                
                if tmp != -1:

                    key = tmp+1
                    if amount_obj not in self.change_map.keys():
                        self.change_map[amount_obj] = key
                    elif amount_obj in self.change_map.keys():
                        if key < self.change_map[amount_obj]:
                            self.change_map[amount_obj] = key
                
                
            # elif el == amount_obj:
            #     self.change_map[amount_obj] = 1
            #     return 1
                
        if amount_obj not in self.change_map.keys():
            self.change_map[amount_obj] = -1
            return -1
        elif amount_obj in self.change_map.keys():
            return self.change_map[amount_obj]
                    
                        
    
    def coinChange(self, coins: List[int], amount: int) -> int:

            
        Solution.changer(self, coins, amount)
        # print(self.change_map,amount)
        if amount not in self.change_map.keys():
            return -1
        
        return self.change_map[amount]

class Solution:
    def coinChange(self, coins:List[int], amount:int) -> int:
        if amount == 0:
            return 0
        start = [0]
        visited = [False]*(amount+1)
        visited[0] = True
        numCoins = 1
        nextStart = []
        while start:
            for v in start:
                for coin in coins:
                    nextVal = v + coin
                    if nextVal > amount or visited[nextVal]:
                        continue
                    elif nextVal == amount:
                        return numCoins
                    else:
                        visited[nextVal] = True
                        nextStart.append(nextVal)
            start, nextStart = nextStart, []
            numCoins += 1
        return -1


from queue import Queue
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        
        q = Queue()
        mem = dict()

        q.put((0, 0))

        while not q.empty():
            curr = q.get()
            
            for c in coins:
                tot = curr[1] + c
                if tot > amount:
                    continue
                if tot == amount:
                    return curr[0] + 1

                if tot in mem and mem[tot] <= curr[0] + 1:
                    continue
                
                q.put((curr[0] + 1, tot))
                mem[tot] = curr[0] + 1

        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        mem = {}
        def helper(amt):
            if amt < 0:
                return -1
            elif amt == 0:
                return 0
            elif amt in mem:
                return mem[amt]
            min_coins = float('inf')
            for coin in coins:
                result = helper(amt - coin)
                if result != -1:
                    min_coins = min(min_coins, 1 + result)
            mem[amt] = min_coins
            return min_coins
        output = helper(amount)
        return -1 if output == float('inf') else output


class Solution:
    def __init__(self):
        self.total_min = 0
        self.change_map = {0:0}
        
    def changer(self, coins_obj, amount_obj):
        
        if amount_obj == 0:
            self.change_map[amount_obj] = 0
            return 0
        if min(coins_obj) > amount_obj:
            self.change_map[amount_obj] = -1
            return -1
            
        rev_coins_obj = list(reversed(coins_obj))
        for el in rev_coins_obj:
            if el <= amount_obj:
                if amount_obj-el in self.change_map.keys():
                    tmp = self.change_map[amount_obj-el]
                else:
                    tmp = Solution.changer(self,coins_obj,amount_obj-el)
                
                if tmp != -1:
                    key = tmp+1
                    if amount_obj not in self.change_map.keys():
                        self.change_map[amount_obj] = key
                    elif amount_obj in self.change_map.keys():
                        if key < self.change_map[amount_obj]:
                            self.change_map[amount_obj] = key
                
                

                
        if amount_obj not in self.change_map.keys():
            self.change_map[amount_obj] = -1
            return -1
        elif amount_obj in self.change_map.keys():
            return self.change_map[amount_obj]
                    
                        
    
    def coinChange(self, coins: List[int], amount: int) -> int:

            
        Solution.changer(self, coins, amount)
        # print(self.change_map,amount)

        return self.change_map[amount]

class Solution:
    def changeCoin(self, coins, amount, change):
        if amount < 0:
            return -1
        if amount == 0:
            return 0
        if amount in change:
            return change[amount]

        costs = []

        for coin in coins:
            cost = self.changeCoin(coins, amount-coin, change)
            if amount-coin in change:
                change[amount-coin] = min(cost, change[amount-coin])
            else:
                change[amount-coin] = cost

            if cost != -1:
                costs.append(cost)

        if len(costs) == 0:
            return -1

        return 1 + min(costs)

    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort()
        coins = [coin for coin in coins if coin <= amount]
        change = {}
        
        
        return self.changeCoin(coins, amount, change)


import numpy as np
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        maximum = amount + 1
        dp = np.full((maximum), maximum)
        dp[0] = 0
        for i in range(1, maximum):
            for j in coins:
                if j <= i:
                    dp[i] = min(dp[i], dp[i - j] + 1)
        if dp[amount] > amount:
            return -1
        return dp[amount]

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        
        dp =  [ [sys.maxsize]*(amount+1) , [sys.maxsize]*(amount+1) ]
        
        
        
        for i in range(1,n+1):
            dp[i%2][0] = 0
            for j in range(1, amount+1):
                dp[i%2][j] = dp[(i-1)%2][j]
                if coins[i-1] <= j:
                    dp[i%2][j] = min(dp[i%2][j-coins[i-1]] + 1 , dp[i%2][j])
        
        #print(dp)
        return dp[n%2][amount] if dp[n%2][amount] < sys.maxsize else -1
                    
                    


class Solution:
    def changeCoin(self, coins, amount, change):
        if amount < 0:
            return -1
        if amount == 0:
            return 0
        if amount in change:
            return change[amount]

        costs = []

        for coin in coins:
            cost = self.changeCoin(coins, amount-coin, change)
            if amount-coin in change:
                change[amount-coin] = min(cost, change[amount-coin])
            else:
                change[amount-coin] = cost

            if cost != -1:
                costs.append(cost)

        if len(costs) == 0:
            return -1

        return 1 + min(costs)

    def coinChange(self, coins: List[int], amount: int) -> int:
        
        change = {}
        
        return self.changeCoin(coins, amount, change)


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        rows = amount + 1
        cols = len(coins) + 1
        
        cache = [[0 for _ in range(cols)] for _ in range(rows)]
        
        for row in range(1, rows):
            cache[row][0] = -1
        
        for row in range(1, rows):
            for col in range(1, cols):
                newAmt = row - coins[col - 1]
                takeValue = cache[newAmt][col] if newAmt >= 0 and newAmt < len(cache) else -1
                takeCoin = 1 + takeValue if takeValue >= 0 else -1
                skipCoin = cache[row][col - 1]
                if skipCoin < 0:
                    cache[row][col] = takeCoin
                elif takeCoin < 0:
                    cache[row][col] = skipCoin
                else:
                    cache[row][col] = min(skipCoin, takeCoin)
        
        return cache[amount][len(coins)]
    


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        self.dp = [[-1]*(amount+1) for _ in range(len(coins) + 1)]
        value = self.solve(coins, len(coins), amount)
        return self.dp[-1][-1] if value != float('inf') else -1

    
    def solve(self, coins, n, amount):
        if amount == 0:
            self.dp[n][amount] = 0
            return 0
        if n == 0:
            self.dp[n][amount] = float('inf')
            return self.dp[n][amount]
        if self.dp[n][amount] != -1:
            return self.dp[n][amount]
        if coins[n-1] <= amount:
            self.dp[n][amount] =  min(1 + self.solve(coins, n, amount - coins[n-1]), self.solve(coins, n-1, amount))
            return self.dp[n][amount]
        else:
            self.dp[n][amount] = self.solve(coins, n-1, amount)
            return self.dp[n][amount]

#https://www.youtube.com/watch?v=1R0_7HqNaW0
import numpy as np
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        #dp[n]: fewest number of coins needed to make n cents
        dp = np.empty(amount+1)
        dp.fill(math.inf)
        dp[0] = 0
        
        #this sorting will affect the average run time and makes it better
        coins = sorted(coins)
        
        for i in range(1, amount+1):
            for c in coins:
                if c <= i:
                    dp[i] = min(dp[i], 1+ dp[i-c])
                else:
                    break
        return -1 if dp[-1] == math.inf else int(dp[-1])

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        INVALID=10**10
        self.ans = INVALID
        def dfs(s, amount, count):
            if amount == 0:
                self.ans = count
                return 
            if s == len(coins):
                return
            coin =coins[s]
            for i in range (amount//coin, -1, -1):
                print(i)
                print (coin)
                print (amount)
                if count+i >=self.ans:
                    break
                dfs(s + 1, amount - i * coin, count+i)
        dfs(0,amount,0)
        if self.ans == INVALID:
            return -1
        else:
            return self.ans


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins = sorted(coins)
        dp = {}
        def in_dict(val):
            return val in dp.keys() and dp[val]
        if not amount:
            return 0
        if coins[0] > amount:
            return -1
        for coin in coins:
            dp[coin] = 1
        for i in range (coins[0], min(amount, coins[-1]) + 1):
            if i in dp.keys():
                continue
            available_coins = [coin for coin in coins if coin <= i]
            possible_min_coins = [1 + dp[i-coin] for coin in available_coins if in_dict(i-coin)]
            dp[i] = min(possible_min_coins) if possible_min_coins else 0
        for i in range(coins[-1]+1, amount+1):
            possible_min_coins = [1 + dp[i-coin] for coin in coins if in_dict(i-coin)]
            dp[i] = min(possible_min_coins) if possible_min_coins else 0
        return dp[amount] or -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = len(coins)
        dp = [[float('inf') for j in range(n+1)] for i in range(amount+1)]
        for j in range(n+1):
            dp[0][j] = 0
        for i in range(1, amount+1):
            for j in range(1, n+1):
                c = coins[j-1]
                if c > i:
                    dp[i][j] = dp[i][j-1]
                else:
                    dp[i][j] = min(dp[i-c][j]+1, dp[i][j-1])
            
        return dp[amount][n] if dp[amount][n] < amount+1 else -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        value1 = [0]
        value2 = []
        nc =  0
        visited = [False]*(amount+1)
        visited[0] = True
        while value1:
            nc += 1
            for v in value1:
                for coin in coins:
                    newval = v + coin
                    if newval <= amount:
                        if not visited[newval]:
                            if newval == amount:
                                return nc
                            visited[newval] = True
                            value2.append(newval)
            value1, value2 = value2, []
        return -1

'''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        min_num = [float('inf') for _ in range(amount + 1)]
        min_num[0] = 0
        
        for c in coins:
            for s in range(c, amount + 1):
                min_num[s] = min(min_num[s], min_num[s - c] + 1)
                
        return -1 if min_num[amount] == float('inf') else min_num[amount]
'''
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        res, seen, curr = 0, set(), {c for c in coins if c <= amount}
        while curr:
            res += 1
            if amount in curr:
                return res
            seen |= curr
            tmp = {n+c for n in curr for c in coins}
            curr = {t for t in tmp if t not in seen and t <= amount}
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        res, seen, curr = 0, set(), {c for c in coins if c <= amount}
        while curr:
            res += 1
            if amount in curr:
                return res
            seen |= curr
            tmp = {n+c for n in curr for c in coins}
            curr = {t for t in tmp if t not in seen and t <= amount}
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: 
            return 0 
        seen = set()
        seen.add(0)
        queue = deque([0])
        count = 0 
        while queue: 
            if amount in queue: 
                return count
            currlen = len(queue)
            for i in range(currlen): 
                node = queue.popleft()
                for c in coins: 
                    if c + node not in seen and c + node <= amount: 
                        queue.append(c + node)
                        seen.add(c + node)
            count += 1
        return -1 
        
        


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
#         if not amount:
#             return 0
        
#         min_coins = [0] + [float('inf')] * amount
        
#         for i in range(amount + 1):
#             if min_coins[i] < float('inf'):
#                 for c in coins:
#                     if i + c <= amount:
#                         min_coins[i + c] = min(min_coins[i + c], min_coins[i] + 1)
        
#         return min_coins[amount] if min_coins[amount] != float('inf') else -1
        
        if not amount:
            return 0
        
        q = deque([amount])
        hs = [False] * amount
        count = 0

        while q:
            l = len(q)
            while l:
                n = q.popleft()
                for c in coins:
                    x = n - c
                    if not x:
                        return count + 1
                    if x > 0 and not hs[x]:
                        hs[x] = True
                        q.append(x)
                l -= 1
            count += 1

        return -1  

import collections
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        '''
        coins = [1, 2, 5], amount = 0
                       ^
                 2  3  6
                 4 
                 10
        '''
        if not amount:
            return 0
        queue = collections.deque(coins)
        visited = set()
        count = 1
        while queue:
            for _ in range(len(queue)):
                curr = queue.popleft()
                if curr == amount:
                    return count
                for next in coins:
                    next += curr
                    if next not in visited and next <= amount:
                        visited.add(next)
                        queue.append(next)
            count += 1
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # BFS
        from collections import deque
        q = deque()
        visited = set()
        q.append(amount)
        visited.add(amount)
        steps = 0
        while q:
            for _ in range(len(q)):
                poped = q.popleft()
                if poped == 0:
                    return steps
                for coin in coins:
                    new = poped-coin
                    if new not in visited and new>=0:
                        q.append(new)
                        visited.add(new)

            steps += 1
        return -1




class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[0, 0]]
        visited = {0}
        step = 0
        for node, step in queue:
            for coin in coins:
                if node + coin in visited: continue
                if node + coin == amount: return step + 1
                if node + coin < amount:
                    queue.append([node + coin, step + 1])
                    visited.add(node + coin)
        return -1
        '''dp = [0] + [float('inf')] * amount
        
        for c in coins:
            for i in range(c, amount + 1):
                dp[i] = min(dp[i], dp[i-c] + 1)
        return dp[-1] if dp[-1] != float('inf') else -1'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[0, 0]]
        visited = {0}
        step = 0
        for node, step in queue:
            for coin in coins:
                if node + coin in visited: continue
                if node + coin == amount: return step + 1
                elif node + coin < amount:
                    queue.append([node + coin, step + 1])
                    visited.add(node + coin)
        return -1
        '''dp = [0] + [float('inf')] * amount
        
        for c in coins:
            for i in range(c, amount + 1):
                dp[i] = min(dp[i], dp[i-c] + 1)
        return dp[-1] if dp[-1] != float('inf') else -1'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        
        q = [0]
        visited = set()
        depth = 0
        while q:
            depth += 1
            level = []
            for curr in q:
                for coin in coins:
                    newvalue = curr + coin
                    if newvalue == amount:
                        return depth
                    if newvalue not in visited and newvalue < amount:
                        visited.add(newvalue)
                        level.append(newvalue)
            q = level
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        # Use a queue to maintain current possible paths towards amount
        if amount == 0:
            return 0
        que = deque()
        seen = set()
        res = -1
        
        for coin in coins:
            if coin <= amount:
                que.append(coin)
                seen.add(coin)
                
        count = 1
        while que:
            prev = len(que)
            while prev > 0:
                cur = que.popleft()
                if cur == amount:
                    return count
                for coin in coins:
                    newamount = cur + coin
                    if newamount not in seen and newamount <= amount:
                        que.append(newamount)
                        seen.add(newamount)
                prev -= 1
            count += 1
        return res
                


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        q = [0]
        depth = 0
        visited = set()
        while q:
            level = []
            depth += 1
            for curr in q:
                for coin in coins:
                    newvalue = curr+coin
                    if newvalue == amount:
                        return depth
                    elif newvalue not in visited and newvalue < amount:
                        visited.add(newvalue)
                        level.append(newvalue)
            q = level
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        seen = level = {0}
        coin_n = 0
        
        while level:
            if amount in level:
                return coin_n
            
            level = {val + coin for val in level for coin in coins if val+coin <= amount} - seen
            seen |= level
            
            coin_n += 1
        
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        coins.sort(reverse = True)
        
        count = 0
        self.maxcount = float('inf')
        
        self.dfs(coins, count, amount)
        
        return -1 if self.maxcount == float('inf') else self.maxcount
    
    def dfs(self,coins,count,amount):
        if amount == 0: self.maxcount = min(self.maxcount,count)
        
        for i in range(len(coins)):
            if amount >= coins[i] and count + math.ceil(amount/coins[i])<self.maxcount:
                self.dfs(coins[i:],count+1,amount-coins[i])

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        
        
        
        
        if amount == 0: return 0
        queue = [[0, 0]]
        visited = {0}
        step = 0
        for node, step in queue:
            for coin in coins:
                if node + coin in visited: continue
                if node + coin == amount: return step + 1
                elif node + coin < amount:
                    queue.append([node + coin, step + 1])
                    visited.add(node + coin)
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[0, 0]]
        visited = {0}
        num = 0
        for pos, num in queue:
            for c in coins:
                if pos + c in visited: continue
                if pos + c == amount: return num + 1
                if pos + c < amount:
                    queue.append([pos + c, num + 1])
                    visited.add(pos + c)
        return -1
        '''dp = [0] + [float('inf')] * amount
        
        for c in coins:
            for i in range(c, amount+1):
                dp[i] = min(dp[i], dp[i-c] + 1)
        return dp[-1] if dp[-1] != float('inf') else -1'''

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0

        value1 = [0]
        value2 = []
        nc =  0
        visited = [False]*(amount+1)
        visited[0] = True
        while value1:
            nc += 1
            for v in value1:
                for coin in coins:
                    newval = v + coin
                    if newval == amount:
                        return nc
                    elif newval > amount:
                        continue
                    elif not visited[newval]:
                        visited[newval] = True
                        value2.append(newval)
            value1, value2 = value2, []
        return -1

from collections import deque
class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = deque([[0, 0]])
        visited = {0}
        step = 0
        while queue:
            node, step = queue.popleft()
            for coin in coins:
                if node + coin in visited: continue
                if node + coin == amount: return step + 1
                elif node + coin < amount:
                    queue.append([node + coin, step + 1])
                    visited.add(node + coin)
        return -1

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount <0 or not coins: 
            return -1
        if not amount: 
            return 0
        stack,lvl,visited = [0],0,set()
        while stack:
            new_lvl = []; 
            lvl+=1
            for i in stack:
                for c in coins:
                    new = i+c
                    if new == amount:
                        return lvl
                    if new not in visited:
                        visited.add(new)
                        new_lvl.append(new)
            if min(new_lvl)>amount:
                return -1
            stack = new_lvl

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0:
            return 0
        myQueue = [[0,0]]
        reachedMap = {0}
        for value, num_coins in myQueue:
            for coin in coins:
                if coin+value in reachedMap:
                    continue
                if coin + value == amount:
                    return num_coins + 1
                if coin+value < amount:
                    reachedMap.add(value+coin)
                    myQueue.append([coin+value, num_coins + 1])
        return -1


class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        if not coins:
            return -1
        
        memo = {}
            
        stack = [amount]
        l = 0
        while stack:
            tmp = []
            for remain in stack:
                if remain < 0:
                    continue
                
                for coin in coins:
                    nxt = remain-coin
                    if nxt == 0:
                        return l+1
                    if nxt in memo:
                        continue
                    else:
                        memo[nxt] = 1
                    tmp += [nxt]
            stack = tmp
            l += 1
        return -1
            
                
                
                



class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount == 0: return 0
        queue = [[0, 0]]
        visited = {0}
        step = 0
        for node, step in queue:
            for coin in coins:
                if node + coin in visited: continue
                if node + coin == amount: return step + 1
                elif node + coin < amount:
                    queue.append([node + coin, step + 1])
                    visited.add(node + coin)
        return -1