感谢:母函数模板解释
母函数模板解释
母函数模板
1;母函数应用于——————形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题.。现在我们先讨论普通生成函数;
2;定义;
(1+x)^n = 1 + C(n,1)x +C(n,2)x^2 + C(n,3)x^3+~~~~~~~~~+C(n,n)^n;
==> G(x)=a0 + a1x + a2x^2 + ~~~~~ + anx^n;
函数G(X)就称序列a0,a1,a2,a3~~~~~an的母函数;
看一个经典的例子;
例一;
有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?
考虑用母函数来解决这个问题:
我们假设x表示砝码,x的指数表示砝码的重量,这样:
1个1克的砝码可以用函数1+x表示,
1个2克的砝码可以用函数1+x∧2表示,
1个3克的砝码可以用函数1+x∧3表示,
1个4克的砝码可以用函数1+x∧4表示
解释;以1+x∧2为例;
2克的砝码有两种状态,取or不取; 1*x^0+1*x^2;就是1+1*x^2;
看联系;G(x) = (1+x^1)(1+x^2)(1+x^3)*(1+x^4);每个括号内都表示每种砝码的情况。
=1+x+x^2+2*x^3+2*x^4+2*x^5+2*x^6+2*x^7+x^8+x^9+x^10;
从最后面的式子可以知道;可以称出1g到10g,前面的系数就是方案数。指数表示可以称出的重量。
列如5g就找到x系数为5的式子;2*x^5系数为2则有2种方案可以称出5g;
例二;
用1分,2分,3分的邮票可以贴出数值的方案数。
该例子对比例一的区别,这里是无限的每种邮票都可以;1~无限。
=======G(x)=(1+x+x^2+x^3+……)(1+x^2+x^4+x^6……)(1+x^3+x^6+x^9……)+( ……);
然后就是同样的,
拆开后;前面的系数就是方案数。指数表示可以称出的重量。
对于这种母函数的应用应该懂了吧。
个人理解;就是一句话,把每种列子的每种情况列出相乘就是要求的母函数了,母函数拆开之后就是结果了,幂表示情况,前面的系数表示该种情况下的方案种类;
现在我们考虑的是怎么用代码实现例二的情况呢。就是说我要怎么用用代码实现,输入一个数可以输出这种重量的方案数。
一位大牛说,我们怎么笔算的就用这种思维去实现,然而……还是不会。
先说一下核心思路吧。
**1;要开两个数组,c1,c2;
C1;保存当前得到的多项式各式系数;
C2;保存每次计算是的临时结果;
并且在之后每计算一次完毕之后就要把c2赋值给c1,并且将c2初始化为0;
2;三重循环
第一重;记录它正在与第几个多项式相乘。
第二重;表示c1中每一项;
第三重;被后面相乘的每一项**。
在根据这些去落实代码吧;
while(scanf("%d",&n) != EOF){ for(i = 0; i <= n; i++){//初始化;要注意并不是每次都是这样初始化的。 c1[i] = 1; c2[i] = 0; } for(i = 2; i <= n; i++){ for(j = 0; j <= n; j++){ for(k = 0; k+j <= n; k += i){ //这里的k+=i表示的是表达式i中幂的变化规律。刚好是加i;注意适当变化。 c2[k+j] += c1[j]; } } for(j = 0; j <= n; j++){ c1[j] = c2[j]; c2[j] = 0; } } printf("%d ",c1[n]); } ;
分析代码;
1;
for(i = 0; i <= n; i++){
//初始化;要注意并不是每次都是这样初始化的。
c1[i] = 1; c2[i] = 0; }
初始化;这样初始化的条件是每项都有从1到n克都存在的情况下,不能存在中间有一项没有这种情况。
列如例子2082就是不能这样的,他中间存在没有的情况。
2;三重循环;
for(i = 2; i <= n; i++){//表示第i个表达式; for(j = 0; j <= n; j++){//表示、c1中的每一项。结果是幂; for(k = 0; k+j <= n; k += i){ //表示与第i个表达式中的幂为k的项相乘; c2[k+j] += c1[j];//这里是累加。
看另外一种模板;代码出自于杭电2082;
memset(c1, 0, sizeof(c1)); memset(c2, 0, sizeof(c2)); c1[0]=1;//有些是0,则不能套用模板中的都等于1; //c1[0]=1;相当于入口、 for(i = 1; i <= 26; i++){ for(j = 0; j <= 50; j++){//c1的各项的指数 for(k = 0; k <= x[i] && j+k*i <= 50; k++){ c2[j+k*i] += c1[j]; }//这里的k表示表达式中的第k项,并且该项的幂是k*i; } for(j = 0; j <= 50; j++){ c1[j] = c2[j]; c2[j] = 0; } }
对比一下上个代码;
区别;
1;初始化那里不同,这里只是c1[0]=1;而不是全部初始化为1;因此这样的初始化适用范围更广,可以是连续的情况。
2;第三重循环那里不同,不再是k+=I;而是k++了。然而这里的区别很大,。
这里的k表示的是第i个表达式中的第k项,并且该项的幂是k*i**;
前面那个代码k表示的是与第i个表达式中的幂为k的项相乘。
一个表示k项一个表示幂为k,这里注意区分。