给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

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

思路

一道简单的动规题。状态转移方程为

class Solution {
public int coinChange(int[] coins, int amount) {
if (amount == 0) return 0;
// dp[i] = min(dp[amount - nums[0]]...dp[amount-nums[n-1]]) + 1;
int[] dp = new int[amount+1];
int min = 0x7FFFFFFF;
// 设置递归基,所有coin只需1枚
for (int coin : coins) {
if (coin > amount) continue;
dp[coin] = 1; min = Math.min(min, coin);
}
System.out.println(min);
if (amount < min) return -1;
// 从0元到面额最小的数值都不存在组合
for (int i = 1; i < min; i++) dp[i] = -1;
// 从最小的面额开始更新dp数组
for (int i = min; i <= amount; i++) {
// 遇到数值正好为硬币面额的话跳过
if (dp[i] == 1) continue;
int minValue = 0x7FFFFFFF;
for (int coin : coins) {
// 从当前的amount中减去coin后的剩余值
int left = i - coin;
if (left >= 0 && dp[left] != -1)
minValue = Math.min(minValue, dp[left]);
}
dp[i] = minValue == 0x7FFFFFFF ? -1 : minValue + 1;
}

System.out.println(Arrays.toString(dp));
return dp[amount];

}
}

复杂度

  • 时间复杂度:
  • 空间复杂度:$O(n