面试题 08.11. 硬币 (Java)
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
示例1:
输入: n = 5
输出:2
解释: 有两种方式可以凑成总金额:
5=5
5=1+1+1+1+1
示例2:
输入: 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
说明:
注意:
你可以假设:
0 <= n (总金额) <= 1000000
思路I:二维数组
可以看成是两个数组(一个是硬币金额,一个是总金额),String其实也是数组。两个数组想到=>动态规划。
dp[i][j]表示遍历到硬币i,金额j时共有几种方法。
状态转移方程:如果不选择当前硬币i,那么有dp[i-1][j]种方法;如果选择,那么有dp[i][j-coins[i]]种方法。
class Solution { public int waysToChange(int n) { int[] coins = new int[4]; coins[0] = 1; coins[1] = 5; coins[2] = 10; coins[3] = 25; int[][] dp = new int[4][n+1]; for(int i = 0; i < n+1; i++){ dp[0][i] = 1; } for(int i = 0; i < 4; i++){ dp[i][0] = 1; } for(int i = 1; i < 4; i++){ for(int j = 1; j <= n; j++){ if(j-coins[i] >= 0) { dp[i][j] = (dp[i-1][j] + dp[i][j-coins[i]]) % 1000000007; } else { dp[i][j] = dp[i-1][j]; } } } return dp[3][n]; } }
思路II: 遍历到i,j时,只与上一行j位置的值有关,所以当前行可以复用上一行结果,从而精简成一维数组。
class Solution { public int waysToChange(int n) { int[] coins = new int[4]; coins[0] = 1; coins[1] = 5; coins[2] = 10; coins[3] = 25; int[] dp = new int[n+1]; for(int i = 0; i < n+1; i++){ dp[i] = 1; } for(int i = 1; i < 4; i++){ for(int j = 1; j <= n; j++){ if(j-coins[i] >= 0) { dp[j] = (dp[j] + dp[j-coins[i]]) % 1000000007; } } } return dp[n]; } }