硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007);
解题思路:二维动态规划,画矩阵。
1. 行维:币种从1到4(按币值排序后逐渐增加), 列维:面值逐渐增加从0到n;
0 | 1 | 2 | 3 | 4 | 5 | ... | i-1 | i | n | |
1 (+1分) | 1 | 1 | 1 | 1 | 1 | 1 | ||||
2 (+5分) | 1 | 1 | 1 | 1 | 1 | 2 | ||||
3 (+10分) | 1 | 1 | 1 | 1 | 1 | 2 | ||||
4 (+25分) | 1 | 1 | 1 | 1 | 1 | 2 |
2. 动态方程:dp[row][col] = dp[row][col-coins[row]] + dp[row-1][col];
dp[row][col] 表示当增加到第row个币种时,获取col面值有多少种方式;有两种可能,一使用新加的币种,二是不使用新加的币种
一使用新加的币种,则可能性跟 “col-当前新加币值”时是一样的:dp[row][col-coins[row]] ;
二是不使用新加的币种,则可能性等于上一次币种凑出面值的可能性;dp[row-1][col];
3. 初始和边界条件:
当币种1时,各种面值可能性为1;
当面值为1时,各种币种可能性都为1;
特殊:当面值为0时,各种币种可能性都为1,考虑减币值case,比如dp[2][5] = dp[2][0] + dp[1][5]
当面值小于新加币值时,当前币值不能使用;
代码:
/**
* @param {number} n
* @return {number}
*/
var waysToChange = function(n) {
const coins = [0,1,5,10,25];
const dp = [[],[],[],[],[]];
for(let coin = 1 ; coin <5;coin++){ // 币种
for(let i= 0;i<=n;i++){ // 面值
if(coin <= 1) {
dp[coin][i]= 1;
}else if(i <= 1){
dp[coin][i]= 1;
}else if(i >= coins[coin]){
dp[coin][i] = dp[coin-1][i]+ dp[coin][i-coins[coin]];
}else{
dp[coin][i] = dp[coin-1][i]
}
}
}
return dp[4][n] % 1000000007;
};