Give you an integer array coins , representing coins of different denominations; and an integer amount , representing the total amount.

minimum number of coins required to make up the total amount. If no coin combination can make up the total amount, return -1 .

You can think that the number of each coin is unlimited.

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

Problem-solving ideas

Some people may think of the greedy method for this problem, and prefer to use coins with large denominations to collect it, but greedy cannot get the optimal solution. For example, the following example:

int[] coins = {1, 2, 5, 7, 10};
int amount = 14;

If you use the greedy method, you must first use 10 , and then you need two 2 . A total of 3 coins are needed. But in fact, you only need two coins with a 7 to make up the amount of 14 Therefore, this problem requires the idea of dynamic programming.

From the following table, you can see the idea of dynamic programming. For the amount of i that needs to be pieced together, traverse the face value of the coin, find the coin with a face value lower than the amount, coins[j] , and then check whether there is an optimal solution dp[i - coins[j]] dp array, and put all possible The situation is listed, just find the smallest one. The whole process starts from 1 and iterates continuously, and iterates to the required amount.

AmountCircumstances that can be collectedOptimal solution
0-0
111 (Use a coin with a face value of 1)
221 (use a coin with a face value of 2)
31 + dp[2], 2 + dp[1]2 (both are optimal solutions)
41 + dp[3], 2 + dp[2]2(2 + dp[2])
551 (Use a coin with a face value of 5)
61 + dp[5], 2 + dp[4], 5 + dp[1]2 (1 + dp[5] or 5 + dp[1])
771 (Use a coin with a face value of 7)
81 + dp[7], 2 + dp[6], 5 + dp[3], 7 + dp[1]2 (1 + dp[7] or 7 + dp[1])
91 + dp[8], 2 + dp[7], 5 + dp[4], 7 + dp[2]2 (2 + dp[7] or 7 + dp[2])
10101 (Use a coin with a face value of 10)
111 + dp[10], 2 + dp[9], 5 + dp[6], 7 + dp[4], 10 + dp[1]2 (1 + dp[10] or 10 + dp[1])
121 + dp[11], 2 + dp[10], 5 + dp[7], 7 + dp[5], 10 + dp[2]2 (2 + dp[10] or 5 + dp[7] or 7 + dp[5] or 10 + dp[2])
131 + dp[12], 2 + dp[11], 5 + dp[8], 7 + dp[6], 10 + dp[3]3 (both are optimal solutions)
141 + dp[13], 2 + dp[12], 5 + dp[9], 7 + dp[7], 10 + dp[4]2(7 + dp[7])

Reference Code

import java.util.Arrays;

public class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1]; // 初始化 dp 数组,大小为 amount + 1
        Arrays.fill(dp, -1); // 全部元素初始化为 -1
        dp[0] = 0; // 金额 0 的最优解 dp[0]=0

        // 依次计算 1 至 amount 的最优解
        for (int i = 1; i <= amount; i++) {
            // 对于每个金额 i ,遍历面值 coins 数组
            for (int j = 0; j < coins.length; j++) {
                // 需要拼凑的面额 i 比当前面值 coins[j] 大,且金额 i - coins[j] 有最优解
                if (coins[j] <= i && dp[i - coins[j]] != -1) {
                    // 如果当前金额还未计算或者 dp[i] 比当前计算的值大
                    if (dp[i] == -1 || dp[i] > dp[i - coins[j]] + 1) {
                        dp[i] = dp[i - coins[j]] + 1; // 更新 dp[i]
                    }
                }
            }
        }
        return dp[amount]; // 返回金额 amount 的最优解 dp[amount]
    }
}

You can see that the above code nested a lot of for and if statement blocks, I wrote an array method implementation in TypeScript:

function coinChange(coins: number[], amount: number) {
  // 创建长度为 amount + 1 ,全部元素为 -1 的数组
  const dp = Array.from(new Array(amount + 1), () => -1);
  dp[0] = 0;
  // 数组 forEach 只能从头开始迭代,不能从中间某个位置开始
  // 这里还是只能用 for 循环
  for (let i=1; i<=amount; i++) {
    // 遍历所有面值计算方案
    const schemes = coins
      .filter(coin => (coin <= i) && (dp[i - coin] !== -1))
      .map(coin => 1 + dp[i - coin]);
    // 如果方案存在,则使用方案中的最小值
    if (schemes.length) {
      dp[i] = Math.min(...schemes);
    }
  }
  return dp[amount];
}
Using the array method to get rid of a for loop and two if judgments, the performance is definitely not as good as the original, but the semantics are improved, and it is easy to understand

Time complexity: O(M*N) ( M is the amount of money, N is the number of coins)
Space complexity: O(M)


一杯绿茶
199 声望17 粉丝

人在一起就是过节,心在一起就是团圆


引用和评论

0 条评论