题目
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例
输入: n = 10
输出:4
解释: 有四种方式可以凑成总金额:
10=10
10=5+5
10=5+1+1+1+1+1
10=1+1+1+1+1+1+1+1+1+1
思路
这一题很容易想到要动态规划,然后……我就觉得很难了,sigh
用一个二维数组 dp[i][j] 来存是用第i种硬币组合成j金额的方法种数i取0-3,分别代表1,5,10,25
如dp[3][27]=dp[2][27]+dp[3][2]:
dp[3][27]:代表用面值25,10,5,1的硬币组成27金额的可能
dp[2][27]:代表不用面值25只用1,5,10组成27的可能
dp[3][2]:代表用了一个面值25,剩下金额2用1,5,10,25组成的可能
那么要加个判断就是j-conis[i]的金额要大于0,不然就代表第i种币值不能用,如上面例子
dp[3][2]由于2<25,2<10,2<5,所以会一直将dp[i][2]=dp[i-1][2]来保证继续直到dp[0][2],只用面值1组成,那么就只有1种情况了。
参考针对某种硬币,有两种可能性:
(1)不用这种硬币:dp[ i - 1 ][ j ]
(2)用这种硬币:dp[ i ][ j - coins[ i ] ]
结果应为两种情况之和。
状态转移方程为:dp[ i ][ j ] = dp[ i - 1 ][ j ] + dp[ i ][ j - coins[ i ] ]
代码如下
二维数组
class Solution {
public int waysToChange(int n) {
int[][] dp = new int[4][n+1];
int[] coins = {1,5,10,25};
for(int j=0;j<=n;j++){
//i=0,表示只用第一种硬币(1分),此时只有一种方法
dp[0][j] = 1;
}
for(int i=1;i<4;i++){
//j=0,表示0分有几种方法,只有一种,那就是一个硬币都不用
dp[i][0] = 1;
}
for(int i=1;i<4;i++){
for(int j=1;j<=n;j++){
if(j<coins[i]){
//j小于当前这种硬币的面值时,只能不选择
dp[i][j] = dp[i-1][j];
}else{
//选择当前这种硬币+不选择当前这种硬币
dp[i][j] = (dp[i][j-coins[i]]+dp[i-1][j])%1000000007;
}
}
}
return dp[3][n];
}
}
一维数组
其实是二维数组的简化,可以对比着理解
class Solution {
public int waysToChange(int n) {
int[] dp = new int[n+1];
int[] coins = {1,5,10,25};
Arrays.fill(dp,1);
//从i=1开始
for(int i=1;i<4;i++){
for(int j=1;j<=n;j++){
//j小于coins[i]的时候,不能选择当前这种硬币,方法数不会增加
if(j>=coins[i]){
//j大于等于coins[i时,方法数会增加“选择当前这种硬币”的情况
dp[j] = (dp[j-coins[i]]+dp[j])%1000000007;
}
}
}
return dp[n];
}
}