钱币兑换问题
钱币兑换问题:
仅有 n 种指定面值的硬币(c1, c2, c3, …, cn), 将钱 N 兑换成 这些面值的硬币,总共有多少种兑换方法?
递推
最简单的递推的想法: 枚举每种货币的使用次数,当使用 k
次第 n
种硬币凑出了 j
钱数的方案,应当等于只使用了前 n-1
种硬币凑出了 dp[j-k*coin[n]]
钱数,而凑出钱数 money
的总方案,应当等于仅使用了
前 1
中硬币、仅使用的前 2
种硬币、…、仅使用了前 n-1
中硬币、使用了全部n种硬币的方案的总和。暗按照这个想法,直接递推求解即可。
int dp[MAX_N] = {0};
int exchange(int money, int n, int coin[]) {
dp[0] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = money; j >= coin[i]; --j) {
for(int k = j / coin[i]; k > 0; --k) {
dp[j] += dp[j - k*coin[i]];
}
}
}
return dp[money];
}
这个方法还可以进一步简化。在上面的方法中,我们从大往小地枚举钱数,考虑到每种硬币都有无限枚可以选,因此,在选择第 i 中硬币时,我们只需要从一个绝无已经选择第 i
种硬币的可能的情形 开始枚举递推即可。在具体实现上,只要改变总钱数 j
的递推顺序即可。进一步地,采用压缩空间的写法,代码如下:
int dp[MAX_N] = {0};
int exchange(int money, int n, int coin[])
{
dp[0] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = coin[i]; j <= money; ++j) {
dp[j] += dp[j-coin[i]];
}
}
return dp[money];
}
这种方法与求解完全背包的算法的思路有很大的相似之处。
母函数
钱币兑换问题本质上是一个组合问题:以 n 中硬币来组合出制定的钱数。因此,也可以通过母函数的方法来求解钱币组合问题。
\[\begin{aligned} f(n) &= (1+x+x^{c_1}+x^{2c_1}+\dots) \\ &\times (1+x+x^{c_2}+x^{2c_2}+\dots) \\ &\times \dots \\ &\times (1+x+x^{c_n}+x^{2c_m}+\dots) \end{aligned}\]所要求的兑换方案数,即第 $n$ 项的系数,接下来,模拟多项式展开即可。