Design and implement a function to solve a classic dynamic programming problem such as the knapsack problem or coin change problem, and explain your complexity trade-offs. Implement the coin change variant below using coin_change(coins, amount); return the minimum number of coins needed to form amount, or -1 if it is impossible. Coins may be reused, and return 0 when amount is zero. For example, [1, 2, 5], 11 returns 3, while [2], 3 returns -1.
def coin_change(coins, amount):