題目:硬幣。給定數量不限的硬幣,幣值爲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];
}