题目描述
题目链接: Leetcode 322 Coin Change
给定几种coins数额,假定每一种coin可以无限次使用,给定一个数额amount,问能加和成这个数额的最小coin的个数
题目思路
1. 暴力枚举
经典的一道组合类问题,最简单解法就是DFS暴力枚举。但是该解法会出现Time Limit Exceeded.
代码如下:
class Solution {
int[] memo; // memorize min number need to add for each amount
int ans = Integer.MAX_VALUE;
public int coinChange(int[] coins, int amount) {
memo = new int[amount+1];
Arrays.fill(memo, Integer.MAX_VALUE);
dfs(coins, amount, 0);
return ans==Integer.MAX_VALUE? -1: ans;
}
private int dfs(int[] coins, int amount, int cnt) {
if (amount<0) {
return Integer.MAX_VALUE;
}
if (amount==0) {
ans = Math.min(cnt, ans);
}
if (memo[amount]!=Integer.MAX_VALUE) {
return memo[amount];
}
int minCount = Integer.MAX_VALUE;
for (int i=0; i<coins.length; i++) {
int res = dfs(coins, amount-coins[i], cnt+1);
if (res==Integer.MAX_VALUE) continue;
// calculate current amount min number coin needed
minCount = Math.min(minCount, cnt+1);
}
memo[amount] = minCount;
return memo[amount];
}
}
时间复杂度:;空间复杂度:
,N代表树状搜索深度。
2. 记忆化搜索
由于暴力搜索会超时(比如coins [1,2,5], amount=100),我们加入记忆化数组来优化时间复杂度,其实也就是Top-Down的动态规划。引入memo数组,memo[i]代表当amount=i时最少加和coins的数量。在每一次递归时,建立一个局部变量来找出当前amount的最小加和coins,如果memo[i]已经有值那么就直接返回该值,从而大大缩减了时间复杂度。
代码如下:
class Solution {
Integer[] memo; // memorize min number need to add for each amount
public int coinChange(int[] coins, int amount) {
// top-down dfs+memo
memo = new Integer[amount+1];
return dfs(coins, amount, 0);
}
// use Top-Down DP - recursive + memo
private int dfs(int[] coins, int amount, int cnt) {
if (amount<0) {
return -1;
}
if (amount==0) {
return 0;
}
if (memo[amount]!=null) {
return memo[amount];
}
int minCount = Integer.MAX_VALUE;
for (int i=0; i<coins.length; i++) {
// return min number of coins need for amount-coins[i]
int res = dfs(coins, amount-coins[i], cnt+1);
if (res==-1) continue;
// calculate current amount min number coin needed
minCount = Math.min(minCount, res+1);
}
memo[amount] = minCount==Integer.MAX_VALUE? -1: minCount;
return memo[amount];
}
}
时间复杂度:;空间复杂度:
,记忆化数组空间。
3. Buttom Up动态规划
将方法二转化成迭代形式,思路相同,建立一个dp[i]数组,代表amount=i时最小加和coins数量。
代码如下:
class Solution {
public int coinChange(int[] coins, int amount) {
// buttom up - dp
return buttomUp(coins, amount);
}
private int buttomUp(int[] coins, int amount) {
int[] dp = new int[amount+1]; // save min coins needed for each amount
Arrays.fill(dp, Integer.MAX_VALUE);
// init
dp[0] = 0;
for(int i=1; i<=amount; i++) {
for(int j=0; j<coins.length; j++) {
if (i-coins[j]<0) continue;
if (dp[i-coins[j]]==Integer.MAX_VALUE) continue;
dp[i] = Math.min(dp[i], dp[i-coins[j]]+1);
}
}
return dp[amount]==Integer.MAX_VALUE? -1: dp[amount];
}
}
时间复杂度:;空间复杂度:
。