生成函数即母函数,是组合数学中尤其是计数方面的一个重要理论和工具。生成函数有普通型生成函数和指数型生成函数两种,其中普通型用的比较多。形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题。母函数还可以解决递归数列的通项问题(例如使用母函数解决斐波那契数列的通项公式)。
将任意一个序列 a0,a1,a2,a3,⋯,an a 0 , a 1 , a 2 , a 3 , ⋯ , a n 以一个函数的方式联系起来
G(x)=a0x0+a1x1+a2x2+a3x3+⋯+anxn G ( x ) = a 0 x 0 + a 1 x 1 + a 2 x 2 + a 3 x 3 + ⋯ + a n x n
则称 G(x) G ( x ) 称为序列的生成函数
普通型母函数可以解决多重集的组合问题,当你要求解组合问题时,可以将组合数与母函数的系数联系起来
例如我们有一个多重集{ 3⋅a,4⋅b,5⋅c 3 ⋅ a , 4 ⋅ b , 5 ⋅ c },求他的10-组合数
我们可以写成母函数的形式
G(x)=(1+x+x2+x3)(1+x+x2+x3+x4)(1+x+x2+x3+x4+x5) G ( x ) = ( 1 + x + x 2 + x 3 ) ( 1 + x + x 2 + x 3 + x 4 ) ( 1 + x + x 2 + x 3 + x 4 + x 5 )
=(1+2x+3x2+4x3+4x4+3x5+2x6+x7)(1+x+x2+x3+x4+x5) = ( 1 + 2 x + 3 x 2 + 4 x 3 + 4 x 4 + 3 x 5 + 2 x 6 + x 7 ) ( 1 + x + x 2 + x 3 + x 4 + x 5 )
=(1+⋯+3x10+2x10+x10+⋯) = ( 1 + ⋯ + 3 x 10 + 2 x 10 + x 10 + ⋯ )
其中 x10 x 10 的系数为6,所以10-组合数为6
母函数的作用就是把组合时的方法数利用乘法系数的分配操作来直接计算出来
我们再看一个例子,有1g砝码2个,2g砝码1个,4g砝码2个,问称量10g的物体有几种方法
我们可以列出母函数方程
G(x)=(1+x+x2)(1+x2)(1+x4+x8) G ( x ) = ( 1 + x + x 2 ) ( 1 + x 2 ) ( 1 + x 4 + x 8 )
=1+x+2x2+x3+2x4+x5+2x6+x7+2x8+x9+2x10+x11+x12 = 1 + x + 2 x 2 + x 3 + 2 x 4 + x 5 + 2 x 6 + x 7 + 2 x 8 + x 9 + 2 x 10 + x 11 + x 12
所以10g的物体有2种方法
那么我们怎么用程序来模拟这个过程呢?
我们用3层循环进行模拟
for(int i=0;ifor(int j=0;jfor(int k=0;k<=a[i];k++)
c2[j+value[i]*k]+=c1[j]
memcpy(c1,c2,sizeof(c1));
memset(c2,0,sizeof(c2));
}
第一重循环的i代表这里的n个多项式,所以约束条件就是 i<n i < n ,然后第二重循环的j代表前i个已经乘完的多项式的结果,大于当前最大次幂的部分的系数反正为0,约束条件是最后的多项式的最大幂次,所以约束条件就是 j<maxn j < m a x n ,第三重循环k代表下一个加入乘法的多项式,约束条件就是这个多项的个数
还是我们上面的砝码问题,程序模拟的话n=3,maxn就是这些砝码所能构成的最大的重量,然后a[i]储存的就是每一种砝码的数量,value储存的就是每种砝码的价值也就是重量
次幂的乘法会把原来的系数也带上,而次幂的乘法就是次幂与次幂之间的和,c1和c2就是代表次幂,里面存的值也就是方法数
指数型母函数可以解决多重集的排列问题
设 S={n1a1,n2a2,⋯,nkak} S = { n 1 a 1 , n 2 a 2 , ⋯ , n k a k } 为多重集,则 S S 的 r− r − 排列数的指数生成函数为
#include
#include
#include
#include
#include
#include
using namespace std;
double c1[15];
double c2[15];
double a[15];
int num[15];
int main()
{
a[0]=a[1]=1;
for(int i=1;i<=10;i++)
a[i]=a[i-1]*i;
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;iscanf("%d",&num[i]);
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
c1[0]=1;
for(int i=0;ifor(int j=0;j<=m;j++)
for(int k=0;k<=num[i]&&j+k<=m;k++)
c2[j+k]+=c1[j]/a[k];
memcpy(c1,c2,sizeof(c1));
memset(c2,0,sizeof(c2));
}
printf("%.0lf\n",c1[m]*a[m]);
}
return 0;
}