You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.
Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
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
Constraints:
1 <= coins.length <= 121 <= coins[i] <= 231 - 10 <= amount <= 104Why this is a 1D Dynamic Programming problem
1D DP stores answers to sub-problems in a 1D array (or a few variables). Each dp[i] represents the answer to a sub-problem of size i. The solution to the full problem is built bottom-up from these smaller answers, eliminating the exponential redundancy of naive recursion.
Define dp[i] clearly ("what does it mean?"), identify the recurrence relation dp[i] = f(dp[i-1], dp[i-2], …), handle base cases.
Imagine you're climbing stairs. The number of ways to reach step n is ways(n-1) + ways(n-2). If you compute ways(n-1) and ways(n-2) independently by recursion, you recompute ways(1), ways(2), … dozens of times. DP caches them so each is computed exactly once.
From brute force to optimal — understand every step of the journey
The hardest part of DP is NOT the code — it's the definition of dp[i]. Ask yourself: "What is the sub-problem at index i?" Once that's crystal clear, the recurrence relation usually follows naturally. Common definitions: dp[i] = "max sum ending at i", "min cost to reach i", "number of ways to reach i". Write the definition as a comment before writing any code.