最近抽了点时间,把在 codewars 上切的题目整理了下,solutions 同步在了 Github。虽然没啥人看,也算给自己留个纪念吧。
今天要讲的是母函数的应用之整数分解方案数,关于母函数的解释可以自行谷歌百度,因为我觉得,即使不知道母函数的概念,也可以把代码看懂。
先来看这道题:How many ways can you make the sum of a number?
题意很简单,把一个整数拆分开来,有多少种分法,比如 4 就有 5 种:
4
3 + 1
2 + 2
2 + 1 + 1
1 + 1 + 1 + 1
思路是这样的,1-4 四个数字一个一个地拿出来,去求拼成 4 的方案数量。先定义一个数组 a,a[1] 表示拼成 1 的方案数,a[2] 表示拼成 2 的方案数,依次类推。初始时只有 a[0] 为 1,其他都为 0。
先拿出 1 来拼,显然 a[0]-a[4] 均为 1。
再拿出 2 来拼,这时的 a[4] 就可以从 a[2] 得到了,比方说我之前 a[2] 有 N 种,然后 a[4] 又能从 a[2] 加上个 2 拼成,那么 a[4] 的方案数量就得加上 a[2] 的数量了,如果有 a[6],那么 a[6] 也得加上 a[2] 的数量,因为 a[2] 加上 2 个 2 就是 a[6] 了。
母函数模板:
/**
* @param {number} target
* @param {array} arr
* @return {number}
*/
// O(n^3)
function sum (target, arr) {
// 如果要分解的数字小于等于 0,则方案数为 0
if (target <= 0)
return 0;
// 如果第二个参数为空,则可以枚举的数为 1-target
if (!arr) {
arr = [];
for (var i = 1; i <= target; i++)
arr[i] = i;
}
var a = []
, b = [];
for (var i = 0; i <= target; i++)
a[i] = b[i] = 0;
// start
a[0] = 1;
// 枚举可以拼的数字
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j <= target; j++)
for (var k = 0; j + k <= target; k += arr[i])
b[j + k] += a[j];
for (var j = 0; j <= target; j++)
a[j] = b[j], b[j] = 0;
}
return a[target];
}