https://leetcode.com/problems/coin-change/description/
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。
题解:
贪心?
反例:1,5,7 凑10,贪心要1个7和3个1,而最优解是2个5.
动态规划:
确定状态:f(x)表示凑足x的钱最少的硬币数
起始状态:f(0)=0
终止状态:f(amount)
决策:要想使本f(x)最小,每次所有钱币,取最小的 f(x-ci)
无后效性:收益只取决与当前状态和决策
收益表示:f(x)=min{ f(x-ci) }+1 , i=1,2,3…n
递推:
class Solution {
public int coinChange(int[] coins, int amount) {
if (amount < 0 || coins.length == 0) {
return -1;
}
int[] f = new int[amount + 1];// 凑i元最少需要的硬币数
f[0] = 0;
for (int i = 1; i <= amount; i++) {
int min = Integer.MAX_VALUE;
for (int c : coins) {
int j = i - c;// 记录用掉这个钱币,剩余的钱数
if (j >= 0 && f[j] != -1) {// j元也能凑成功
min = Math.min(min, f[j]);
}
f[i] = min == Integer.MAX_VALUE ? -1 : min + 1;//min == Integer.MAX_VALUE 说明花掉这个硬币,不能凑成功,如果凑成功,硬币数+1
}
}
return f[amount];
}
}
递归:(超时)
class Solution {
public int coinChange(int[] coins, int amount) {
cache.clear();
return robot(coins, amount);
}
public static Map<Integer,Integer> cache=new HashMap<Integer,Integer>();
private int robot(int[] coins, int amount) {// 凑amount元最少需要的硬币数
if (amount == 0) {
return 0;
}
if (amount < 0) {
return -1;
}
if(cache.containsKey(amount)){
return cache.get(amount);
}
int min = Integer.MAX_VALUE;
for (int i = 0; i < coins.length; i++) {
int next = robot(coins, amount - coins[i]);
if (next == -1) {//使用该钱币不能构成这个钱数
continue;
}
int num = 1 + next;//使用是个钱币
if (num < min) {//更新最小钱币数
min = num;
cache.put(amount, min);
}
}
if (min == Integer.MAX_VALUE) {//不能凑成
return -1;
} else {
return min;
}
}
}