题目:硬币。给定数量不限的硬币,币值为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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-lcci
思路:将硬币排成一个序列:1、5、10 与 25
1 、得到状态转移方程
利用前 i 种硬币来凑成钱数 v 的共有 f ( i , v ) 中方法,则 有递推公式 f ( i , v ) = f(i -1,v) + f( i-1 , v - ci) + f( i -1 , v - 2*ci) + f( i -1 , v - 3*ci) + ... + f( i -1 , v - k*ci) 其中 k = [v / ci ] (k取不大于 v/ci 的最大整数),
则有 f ( i , v - ci ) = f(i -1,v - ci ) + f( i-1 , v - 2 * ci) + f( i -1 , v - 3*ci) + f( i -1 , v - 4*ci) + ... + f( i -1 , v - k*ci)
即 f ( i , v ) = f(i -1,v) + f ( i , v - ci ) 状态转移方程
2、利用状态转移方程
定义两个长度为 n + 1 的一维数组 d1[n+1] 与 d2[n+1] ,其中 d1数组保存的是 前 i-1种硬币凑成相应下标数目钱的方法数, d2数组保存的是 前 i 种硬币凑成相应下标数目钱的方法数,比如 d1[v] = f ( i-1,v ) d2[v] = f ( i , v )
首先初始化数组 d1 ,d1 所有元素初始化为 1 ,迭代数组 d1 与 d2
nums = {1,5,10,25}
伪代码:
d2[0] = 1
for ( i=1;i<nums.length;i++){
coin = nums[i]
for(j = coin;j<=n;j++){
d2[j] = d1[j] + d2[j - coin]
}
for(j = 0;j<=n;j++){
d2[j] = d1 [j]
}
}
java 代码如下:
public int waysToChange(int n) {
int[] coins = {1,5,10,25};
int[] d1 = new int[n+1];
int[] d2 = new int[n+1];
d1[0] = 1; //凑成钱数0 的方法只有 1 种
d2[0] = 1; //凑成钱数0 的方法只有 1 种
//初始化,只能用一种硬币凑成第一种硬币的整数倍的钱数
for(int j=coins[0];j<=n;j+=coins[0]){
d1[j] = 1;
if(j<coins[1]){
d2[j] = 1; //在小于第二种硬币的钱数,方法数为1
}
}
int mod = 1000000007;
for(int i=1;i<coins.length;i++){
int coin = coins[i];
for(int j=coin;j<=n;j++){
d2[j] = (d1[j] + d2[j-coin])%mod; // f ( i , v ) = f(i -1,v) + f ( i , v - ci ) 状态转移方程
}
for(int j=coin;j<=n;j++){
d1[j] = d2[j]; //本轮迭代的状态值保存到d1中
}
}
return d2[n];
}