题目描述
https://leetcode-cn.com/problems/coin-change-2/
解法
完全背包问题,框架:
- 确定状态,就是问题中的变量有哪些——1、不同种类的硬币;2、总金额数;3、所用硬币数目;然后根据题目,我们只需要使用前两个量就能描述出当前的问题,说明dp数组为二维,且两个维度分别对应1、2变量。
- dp数组下定义:dp[i][j]表示前i种硬币能凑出金额为j的方法数,严格相信这个定义
- 确定递推过程:如果第i个硬币装得下,我可以选择不装,则dp[i][j] = dp[i-1][j],选择装,则p[i][j] = dp[i][j-coins[i-1]],所以这种情况下dp[i][j] = dp[i-1][j]+dp[i][j-conis[i-1]];如果装不下,则dp[i][j] = d[i-1][j]
- 如果无法递推成功,则返回第二步,dp数组定义错误,重新思考dp数组的定义
- 思考边界情况:dp[i][0]就是金额为0的方法,只有一种,就是不凑,按照题目的结果为1,dp[0][j]=0,表示没有金币来凑
最后我们编写代码:
class Solution {
public int change(int amount, int[] coins) {
if(amount==0) return 1;
if( coins==null || coins.length==0) return 0;
int [][]dp = new int[coins.length+1][amount+1];//表示前i种硬币组合为j元的可能方法数
//边界 dp[0][j]=0 dp[i][0]=1
for(int i=0;i<=coins.length;i++){
dp[i][0] = 1;
}
//递推方程
for(int i=1;i<=coins.length;i++){
for(int j=1;j<=amount;j++){
if(j-coins[i-1]>=0){
//当前硬币装得下
dp[i][j] = dp[i-1][j] //不装
+ dp[i][j-coins[i-1]];//装下该硬币,但是因为数量无限个,可以继续装
}else//装不下
dp[i][j] = dp[i-1][j];
}
}
return dp[coins.length][amount];
}
}
然后从解法上看到dp[i][j]和之前的两个状态有关,可以利用状态压缩来降低空间复杂度:
- 一维数组dp[j]
class Solution {
public int change(int amount, int[] coins) {
if(amount==0) return 1;
if( coins==null || coins.length==0) return 0;
int []dp = new int[amount+1];//表示前i种硬币组合为j元的可能方法数
//边界 dp[0][j]=0 dp[i][0]=1
dp[0] = 1;
//递推方程
for(int i=1;i<=coins.length;i++){
for(int j=1;j<=amount;j++){
if(j-coins[i-1]>=0){
//当前硬币装得下
dp[j] = dp[j] //不装
+ dp[j-coins[i-1]];//装下该硬币,但是因为数量无限个,可以继续装
}
// }else//装不下
// dp[j] = dp[j];
}
}
return dp[amount];
}
}