母函数
母函数分为普通型母函数、指数型母函数、L级数等,普通型母函数使用较多。
利用的思想:
把组合问题的加法法则和幂级数的乘幂对应起来。
1.普通型母函数
普通型母函数(即普通型生成函数)用于解决多重集的组合问题。
通过构造一个多项式函数,使多项式各项的系数、次数等与多重集联系起来。
若函数 G(x)= ∑ an * xn = a0+a1* x+a2* x 2+···+an*xn
称G(x)是序列a0,a1,a2,…,an的普通型生成函数
例如有质量分别为1g,3g,5g的砝码各2个,问能称出质量为num的物品的组合方案有多少?
构造母函数G(x)=(1+x+x2)(1+x3+x6)(1+x5+x10)
某一括号内x的指数即为:对于某一种砝码种类,取不同数量的选择下的质量和
转换后G(x)=1+x+x2+x3+x4+2x5+2x6+2x7……
x的指数即为不同组合后的重量和,x的系数为组成重量大小为 x的指数 的方案数
展开过程中(多项式从左往右逐个相乘)可以看成对于同种重量的砝码选择不同数量加入到左边没有这种砝码的状态的过程。
模板如下:
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
int T, m, num, a[10], b[10], c1[51], c2[51];
//c1[i] i的组成方案数
//c2[i] 临时存放每乘一项后的结果
cin>>T;
while (T--) {
cin>>num>>m;//num 目标和
for (int i = 1;i <= m;i++)
cin>>a[i]>>b[i];//a[]保存重量 b[]保存数量
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0] = 1;//第0项为1 最左边乘1对多项式无影响
for (int i = 1;i <= m;i++) {//i表示乘到了第几个括号
for (int j = 0;j <= num;j++)//j表示左边项中每一项x^j的指数j(即不同数量的砝码组成的重量)
{
for (int k = 0;k + j <= num&&k <= b[i]*a[i];k += a[i])
//k是右边项中每一项x^k的指数k 即重量
//重量和大于目标或砝码用完时循环停止
//例如(1+x+x^2)(1+x^2+x^4+x^6+x^8)
//共四个2g砝码 对应指数:2*0=1 2*1=2 2*2=4 2*3=6 2*4=8
c2[j+k]+=c1[j];//c2下标为x的次数 c2[j+k]的值为x^(j+k)的系数
}
memcpy(c1, c2, sizeof(c1));//把c2赋给c1
memset(c2, 0, sizeof(c2));// c2清零
}
cout<<c1[num]<<endl;
}
return 0;
}
2.指数型函数
指数型母函数(即指数型生成函数)用于解决多重集的排列问题。
对序列a0,a1,a2,···an
若元素各不相同则全排列可得n!个不同全排列。
与高中排列组合 C 类似,若a1重复n1次,a2重复n2次···ak重复nk次,且∑nk=n
则有重复元素的序列a0,a1,a2···ak进行全排列所得的不同排列方案数为
n!/n1 ! *n2 ! *n3 !···nk!
构造G(x) = (1+x1/1!+x2/2!+……+xn1/(n1)!) (1+x1/1!+x2/2!+……+xn2/(n2)!)……(1+x1/1!+x2/2!+………+xnk/(nk!))
转换后排列方案数序列a0,a1,a2,···,ak的指数型母函数为
G(x)=a0+a1* x1/1!+a2* x2/2!···ak *xk/k!
模板如下:
#include<iostream>
#include<string.h>
using namespace std;
int c1[52];
void calculate()
{
c1[0]=c1[1]=1;
for(int i=2;i<=10;i++)
c1[i]=i*c1[i-1];
}
int main()
{
int n,m,num[52];
double c[52],temp[52];
calculate();
while(~scanf("%d%d",&n,&m)){
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
for(int i=0;i<=num[1];i++)
c[i]=1.0/c1[i];
for(int i=2;i<=n;i++)
{ //i表示运算到第几个括号
memset(temp,0,sizeof(temp));
for(int j=0;j<=m;j++)
for(int k=0;k<=num[i]&&k+j<=m;k++)
temp[k+j]+=(1.0*c[j]/c1[k]);
for(int j=0;j<=m;j++)
c[j]=temp[j];
}
printf("%.0f\n",c[m]*c1[m]);
}
return 0;
}