这篇文章上次修改于 487 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
ACM程序课算法笔记7——母函数
定义
母函数实际上就是一个多项式和其系数表达的关系,
对于多项式x2 + 2x + 1就是序列1 2 1的母函数
母函数的基础转化
问题
若有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?各有几种可能方案?
构造
对此我们可以构造母函数,对于每个砝码,我们让砝码的重量作为x的指数。
对于4g的砝码,我们可以设置为(1 + x^4)
那么对于这道题我们就可以构造结果
(1 + x) (1 + x^2) (1 + x^3) (1 + x^4) =
1 + x + x^2 + 2x^3 + 2x^4 + 2x^5 + 2x^6 + 2x^7 + x^8 + x^9 + x^10
对于ax^b也就代表对于b克物品有a种方案可以称出来
那么如何解释 (1 + x^4)的含义呢。
对于结果式(1 + x) (1 + x^2) (1 + x^3) (1 + x^4)结果的每一项,都是从这四项中二选一然后相乘得到的。(1 + x^4)也就意味着,要不我取用4g砝码,要不我就不取。
代码
这是一种基础的构造方式,把题目含义转化为多项式相乘,利用程序帮助计算
// 计算 (1 + x) (1 + x^2) (1 + x^3) (1 + x^4)
result[0] = 1; // 代表初始值 为 1 * x ^ 0 + 0 * x ^ 1 + 0 * x ^ 2 ....
for(int i = 1; i < 4; i++){ // 4为多项式的个数
for(int j = 0; j <= 10; j++){ // 10为需要储存多项式结果次数的最大值
temp[j + k] += result[j];
}
for (int j = 0; j <= score; j++)
{
result[j] = temp[j];
temp[j] = 0;
}
}
// result
母函数多项式内无限项
求用1分、2分、3分的邮票贴的某个数值的方案数—— 因邮票允许重复,故母函数为:
G(x) = (1 + x + x^2 + …) (1 + x^2 + x^4 + …) (1 + x^3 + x^6 + …)
由上题总结 对于结果ax^b也就代表对于b克物品有a种方案可以称出来
同样是获得结果多项式对应项的系数
多了无限项的条件,实际上我们计算的x次数只要不超过指定次数即可。例如我们求9以内的邮票的次数,x^4 与 x^6 我们就不用计算储存考虑
问题
整数拆分,就是把整数分解成若干整数的和,比如:3 = 1+1+1, 3 = 1+2 等等;整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。求整数n的拆分数。
构造
对于n来说相当于有1分、2分、3分… n分n种邮票贴出数值为n的方案数,只要构造
G(x) = (1 + x + x^2 + …) (1 + x^2 + x^4 + …) (1 + x^3 + x^6 + …) (1 + x^4 + x^8 + …)… (1 + x^n + x^2n + …)
并求结果即可,此时结果中大于n次的项都不需要计算。
代码
result[0] = 1;
for (i = 1; i <= n; i++) // 代表了n个多项式
{
for (j = 0; j <= n; j++)
for (k = 0; k + j <= n; k += i) // i为当前多项式的每项递增的次数
{ // 当 k + j 比n大,不需要进行计算,高次数不会影响低次数
temp[j + k] += result[j];
}
for (j = 0; j <= n; j++)
{
result[j] = temp[j];
temp[j] = 0;
}
}
cout << result[n] << endl;
母函数多项式无规律系数
对于自定义系数,理解上反而比无限项简单,对于价值为v的物品有n个其多项式就是
(1 + x^v + x^2v + … + x^nv)
或者是系数并不是简单递增,例如平方数等等
问题1
告诉各个物品的各个价格和数量,求他们组合成某个价值数的组合种类有几种。(限制了数量)
代码
// 结构体说明
srtuct Item{
int value;
int num;
}
// 结构体说明 items是该结构体vector
result[0] = 1;
for (Item it:items) // 代表了items.size()个多项式
{
for (j = 0; j <= n; j++)
for (k = 0; k * it.value + j <= n and k <= it.num; k ++) // 增加限制条件 k <= it.num
{
temp[k * it.value + j] += result[j];
}
for (j = 0; j <= n; j++)
{
result[j] = temp[j];
temp[j] = 0;
}
}
cout << result[n] << endl;
问题2
同样是硬币组合,现在硬币只有1,4,9,16等平方数的种类直到289,对于一个价值有几种组合。
代码
int coins = {1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289};
result[0] = 1;
for (int value:coins) // 代表了coins.size()个多项式
{
for (j = 0; j <= n; j++)
for (k = 0; k * value + j <= n; k ++)
{
temp[k * value + j] += result[j];
}
for (j = 0; j <= n; j++)
{
result[j] = temp[j];
temp[j] = 0;
}
}
cout << result[n] << endl;
母函数多项式负系数
问题
给你一个天平,告诉你有的砝码,求该天平无法称出的重量有哪些
构造
有意思的地方在于,天平两段都可以放置砝码,例如5,2实际上还可以称出3.
对于这种问题,实际上就是砝码之间可以相减,我们的砝码多了一种选择方式,原本的砝码是不取,取一个,取多个。现在的砝码多了一个减去的作用,该作用在多项式中便体现为负的系数,对于砝码重量为v,多项式为
(x^-v + 1 + x^v)
代码
// 结构体说明
srtuct Item{
int value;
int num;
}
// 结构体说明 items是该结构体vector
result[0] = 1;
for (Item it: Item) // 代表了n个多项式
{
for (j = 0; j <= n; j++)
for (k = -it.num; k * it.value + j <= n and k * it.value + j >= 0 and j <= it.num; k ++) // k + j 要大于等于 0
{
temp[k * it.value + j] += result[j];
}
for (j = 0; j <= n; j++)
{
result[j] = temp[j];
temp[j] = 0;
}
}
cout << result[n] << endl;