目录
1.题目
2.思路
如果数据范围小一点,直接普通的BFS,DFS都能解决。我们先来分析一下时间复杂度,假如是普通的BFS而言,硬币数组的长度为n, amount 最大值为10000.那么我们每一次都可以选择n个硬币。我们最多选择的次数为amount / coins[]的最小值 + 1,令其为cnt.相当于总共需要变量cnt次。最终时间为n ^ cnt。n最大值为12的话,coins数组最小值为1,那么cnt最大为10000.所以最终时间复杂度为(12 ^ 10000),所以妥妥的超时...........用普通的DFS也是一样的,会超时。
超时的根本原因在于计算了许多重复的值,而这些值没有被保存下来,所以导致时间复杂度非常高。这样优化的思路就可以从普通的搜索转化为记忆化搜索,然后记忆化搜索 和 动态规划是可以相互转化的。
记忆化搜索,相当于从上往下递归。动态规划相当于从下往上递推。个人比较喜欢写的动态规划。动态规划三部曲。
- 定义:dp[i] :代表组成和为i所需的最少硬币数量。(假如组不成这个数,那么赋值一个很大的数即可。通常赋值整数的最大值)
- 初始化:dp[0] = 0.其他所有的数dp[i] = max
- 状态转移:遍历所有的硬币数组coins[],dp[i] = Math.min(dp[i],dp[i - coin] + 1);
时间复杂度——O(n * amount)
一维动态规划
空间复杂度——O(amount + 1)
借助dp[]数组
class Solution {
public int coinChange(int[] coins, int amount) {
// dp[i]:组成和为i的最少的硬币个数。
int n = coins.length;
int[]dp = new int[amount + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for(int i = 1; i <= amount; i++){
for(int coin : coins){
if(i - coin >= 0){
if(dp[i - coin] >= Integer.MAX_VALUE)continue;
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] >= Integer.MAX_VALUE ? -1 : dp[amount];
}
}
注意这里如果是赋值为整数的最大值的话,容易越界,比如原本dp[i-coin] = 2147483647,然后再加1,由于整数越界,就会导致结果变成-2147483648。这样再和dp[i]取最小值的话就会出问题。
所以在初始化的时候,可以选择赋值max = amount + 1.因为这个数已经大于最大的硬币个数了,因为coin最小值为1。并且这样不会越界。
class Solution {
public int coinChange(int[] coins, int amount) {
// dp[i]:组成和为i的最少的硬币个数。
int n = coins.length;
int[]dp = new int[amount + 1];
Arrays.fill(dp, amount + 1); //如果这里设置成Integer.MAX_VALUE,小心越界。因为后面有+1操作,越界后就变成负数,然后取最小的话,答案就会出错。
// 因为硬币最少都是1,所以最多需要amount个数,所以这里赋值一个不可能的数amount+ 1
dp[0] = 0;
for(int i = 1; i <= amount; i++){
for(int coin : coins){
if(i - coin >= 0){
// if(dp[i - coin] >= Integer.MAX_VALUE)continue; //如果上面数组赋值为Integer.MAX_VALUE,这里可以提前判断退出,也不会出错。
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}
3.结果