322. 零钱兑换
题目
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
思路
定义f[i]表示当金额为i时,所需要的最小硬币的个数。以上面例子中的数据为例,转移方程为 f [ i ] = m i n { f [ i − 1 ] + 1 , f [ i − 2 ] + 1 , f [ i − 5 ] + 1 } f[i] = min\{f[i-1]+1,f[i-2]+1,f[i-5]+1\} f[i]=min{f[i−1]+1,f[i−2]+1,f[i−5]+1}
代码
public int coinChange(int[] coins, int amount) {
int m = coins.length;
int f[] = new int[amount+1];
f[0] = 0;
for(int i=1;i<=amount;i++){
f[i] = Integer.MAX_VALUE;
for(int j=0;j<m;j++){
//f[i-coins[j]]=Integer.MAX_VALUE 表示f[i-coins[j]]是无法凑出来的,
//这种情况下忽略就行了
if(i>=coins[j] && f[i-coins[j]]!=Integer.MAX_VALUE){
f[i] = Math.min(f[i],f[i-coins[j]]+1);
}
}
}
if(f[amount]==Integer.MAX_VALUE){
return -1;
}else{
return f[amount];
}
}
进阶
- 零钱兑换 II
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
示例 1:
输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
进阶思路
定义状态f[i][j]表示当总金额为j时,用前i个硬币,可以凑出来的硬币组合数。状态转移函数为
f
[
i
]
[
j
]
=
f
[
i
]
[
j
−
c
o
i
n
[
i
]
]
+
f
[
i
−
1
]
[
j
]
f[i][j] = f[i][j-coin[i]]+f[i-1][j]
f[i][j]=f[i][j−coin[i]]+f[i−1][j],具体来说,因为硬币是可以重复使用的,所以
f
[
i
]
[
j
−
c
o
i
n
[
i
]
]
f[i][j-coin[i]]
f[i][j−coin[i]]表示至少使用一次第i枚硬币所得到的组合数,而
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]表示不适用第i枚硬币所得的组合数。这样的话,正好覆盖了所有的情况。
代码
public int change(int amount, int[] coins) {
int n = coins.length;
int f[][] = new int[n+1][amount+1];
f[0][0]=1; //初始化状态
for(int i=1;i<=n;i++){
for(int j=0;j<=amount;j++){
if(j==0){
f[i][j] = 1; //金额为0时,什么都不取就可以满足,因此只有这一种情况
}else{
if(j>=coins[i-1]){
f[i][j] = f[i][j-coins[i-1]]+f[i-1][j];
}else{
f[i][j] = f[i-1][j];
}
}
}
}
return f[n][amount];
}