组合数学 —— 母函数

【概述】

某个序列的母函数是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息。

给定数列 \{a_0,a_1,a_2,...,a_n,...\},构造一个函数 F(x)=a_0f_0(x)+a_1f_1(x)+...+a_nf_n(x)+...,称 F(x) 为数列 \{a_0,a_1,a_2,...,a_n,...\} 的母函数,其中,序列 f_0(x),f_1(x),...,f_n(x),... 只作为标志用,称为标志函数

标志函数最重要的形式是 x^n,这种情况下的母函数一般形式为:F(x)=a_0+a_1x+a_2x^2+...+a_nx^n+...

例如:(1+x)^n=1+C(n,1)x+C(n,2)x^2+...+C(n,n)x^n 就是序列 \{C(n,0),C(n,1),C(n,2),...,C(n,n)\} 的母函数

也就是说,可以利用 (1+x)^n 来讨论序列\{C(n,k)\} 的性质,此外还可以引入适当的函数,将问题简化,把复杂的问题变成形式上的初等代数运算。

母函数可以分成许多种,如:普通母函数、指数母函数、L 级数、贝尔级数、狄利克雷级数等等。

【普通型母函数】

1.杨辉三角

杨辉三角中第 n 行的数字就是 (1+x)^n 的展开式从低项到高项的各项系数。

而 (1+x)^n=C_n^01^nx^0+C_n^11^{n-1}x^1+...+C_n^r1^{n-r}x^r+...+C_n^{n-1}1^{1}x^{n-1}+C_n^n1^{0}x^n

                    =C_n^0+C_n^1x+...+C_n^rx^r+...+C_n^{n-1}x^{n-1}+C_n^nx^n

所以,有如下形式的杨辉三角:

组合数学 —— 母函数_第1张图片

将 (1+x) 与选择物品联系起来,在构造和分析一个母函数时,1 一般看成 x^0,用于表示没有选取一个物品,x^1 可以看成选择了一个物品。因此 (1+x)^n 可以对应于从 n 件物品中选取了若干件物品的情况。

在一个具体的物品选择中,如果没有选择第 i 件物品,则相当于从第 i 个括号中选取了 x^0 ,如果选择了第 i 件物品,这相当于从第 i 个括号中选择了 x^1,这样,在 (1+x)^n 的展开式中,x^i 前面的系数就是从 n 件物品选取了 i 件物品的所有组合情况的总数,即 C(n,i)

2.母函数定理

设从 n 元集合 S=\{a_1,a_2,...,a_n\} 中取 k 个元素的组合是 b_k,若限定元素 a_i 出现次数的集合为 M_i(1\leqslant i\leqslant n),则该组合数序列的母函数为:\prod_{i=1}^{n}(\sum_{m\in M_i}x^m)

3.常见普通母函数

\frac{1}{1-x}=1+x+x^2+x^3+...

\frac{1}{(1-x)^2}=\frac{1}{(1-x)*(1-x)}=1+2x+3x^2+...

\frac{1}{(1-x)^n}=1+nx+(n*\frac{n+1}{2!})x^2+(n*\frac{(n+1)*(n+2)}{3!})x^3+...

3.解题思路

普通母函数主要是来求组合的方案数

首先写出表达式,通常是多项的乘积,每项由多个 x^y 组成。

通用表达式为:

\left\{\begin{matrix} x^{v[1]*n1[1]}+x^{v[1]*(n1[1]+1)}+x^{v[1]*(n1[1]+2)}+...+x^{v[1]*n2[1]} \\ x^{v[0]*n1[0]}+x^{v[0]*(n1[0]+1)}+x^{v[0]*(n1[0]+2)}+...+x^{v[0]*n2[0]} \\ ... \\ x^{v[k]*n1[k]}+x^{v[k]*(n1[k]+1)}+x^{v[k]*(n1[k]+2)}+...+x^{v[k]*n2[k]} \end{matrix}\right.

其中,各变量含义如下:

  • k 问题中的物品种类数
  • v[i] 表示该乘积表达式第 i 个因子的权重,对应于具体问题的每个物品的权重
  • n1[i] 表示该乘积表达式第 i 个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个(一般为 0)
  • n2[i] 表示该乘积表达式第 i 个因子的终止系数,对应于具体问题中的每个物品的最多个数,即最多要取多少个(一般为 INF)

解题的关键是要确定 v、n1、n2 数组的值,通常情况下,n1 的值都为 0,n2 的值为无限大(INF)。

然后实现表达式相乘,从第一个因子开始乘,直到最后一个为止,此时常用一个循环来解决,每次迭代的计算结果存入数组 a[i] 中,计算结束后,a[i] 表示权重 i 的组合数,即对应具体问题的组合数。

在循环内部,将每个因子的每项与数组 a[] 中的的每项相乘,加到一个临时数组 b[] 的对应位,最后再将 b 赋给 a 即可(此处有两层循环,加上最外层循环,总共三层)。

最后的结果即为 a[P],其中 P 为可能的最大指数。

4.例题

问题:给出 5 张 1 元,4 张 2 元,3 张 5 元的纸币,要得到 15 元,问有多少种组合?

思路:

首先可以确定 k=3,然后确定 v、n1、n2 三个数组,即:v[3]={1,2,5},n1[3]={0,0,1},n2[3]={5,4,3}

之后写出表达式,即:(1+x^2+x^3+x^4+x^5)(1+x^2+x^4+x^6+x^8+x^10)(x^5+x^10+x^15),可以发现最大的可能指数 P=15

最后套用版子即可。

int a[N];//权重为i的组合数
int b[N];//临时数组
int P;//最大指数
int v[N],n1[N],n2[N];
void cal(int k){
    memset(a,0,sizeof(a));
    a[0]=1;
    for(int i=1;i<=k;i++){//循环每个因子
        memset(b,0,sizeof(b));
        for(int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循环每个因子的每一项,若n2是无穷,则j<=n2[i]可以去掉
            for(int k=0;k+j*v[i]<=P;k++)//循环a的每个项
                b[k+j*v[i]]+=a[k];//把结果加到对应位
        memcpy(a,b,sizeof(b));//b赋值给a
    }
}
int main(){
    v[1]=1;  v[2]=2;  v[3]=5;
    n1[1]=0; n1[2]=0; n1[3]=1;
    n2[1]=5; n2[2]=4; n2[3]=3;
    P=15;
    cal(3);
    cout<

5.通用模版

通常情况下,使用模版一即可,但如果数据规模比较大,就要使用模版二。

1)模版一

P 是可能的最大指数。拿上述例题来说,若要求 15 元有多少组合,那么 P 就是 15,若问最小的不能拼出的数值,那么 P 就是所有钱加起来的和。

此外,如果 n2 是无穷,那么第二层循环条件 j<=n2[i] 可以去掉。

int a[N];//权重为i的组合数,a[P]即为结果
int b[N];//临时数组
int P;//最大指数
int v[N],n1[N],n2[N];
void cal(int k){
    memset(a,0,sizeof(a));
    a[0]=1;
    for(int i=1;i<=k;i++){//循环每个因子
        memset(b,0,sizeof(b));
        for(int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循环每个因子的每一项,若n2是无穷,则j<=n2[i]可以去掉
            for(int k=0;k+j*v[i]<=P;k++)//循环a的每个项
                b[k+j*v[i]]+=a[k];//把结果加到对应位
        memcpy(a,b,sizeof(b));//b赋值给a
    }
}

2)模版二

用一个 last 变量记录目前最大的指数,这样只需要在 0..last 上进行计算,从而大大提高效率。

int a[N];//权重为i的组合数,a[P]即为结果
int b[N];//临时数组
int P;//最大指数
int v[N],n1[N],n2[N];
void cal(int k){
    a[0]=1;
    int last=0;
    for(int i=0; i

【指数型母函数】

1.定义

对于序列 \{a_0,a_1,a_2, ...\} ,函数 G_e(x)=a_0+\frac{a_1}{1!}x+\frac{a_2}{2!}x^2+...+\frac{a_k}{k!}x^k+... 称为序列 \{a_0,a_1,a_2, ...\} 对应的指数母函数。

指数型母函数可以理解为:对于 \frac{x^i}{j!} 表示在一个方案中某个元素出现了 j 次,而在不同的位置中的 j 次出现是相同的,所以在排列计算总数时,只应算作一次,由排列组合的知识知道,最后的结果应该除以 j!

指数型母函数在使用过程中,一般会用到高等数学中的 e^x 的泰勒展开式:

e^x=\sum_{n=0}^\infty \frac{x^n}{n!}=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+...+\frac{x^n}{n!}+...

因此,指数型母函数可以简化为:G_e(x)=b_0+b_1*\frac{x}{1!}+b_2*\frac{x^2}{2!}+b_3*\frac{x^3}{3!}+...,其中,b_i 是所需的结果。

2.应用

指数型母函数常用于求多重排列数,即:有 n 种物品,已知每种物品的数量为 k_1,k_2,...,k_n 个,求从中选出 m 件物品的排列数。

对于 n 个元素,其中 a_0,a_1,a_2, ...,a_n 互不相同,进行全排列,可得 n! 个不同的排列。

若其中某个元素 a_i 重复了 n_i 次,那么全排列出来的必然有重复元素,而其中真正不同的排列数应为:\frac{n!}{n_i!},即重复度为 ni!

同理,a_1 重复了 n_1 次,a_2 重复了 n_2 次,...,a_k 重复了 n_k 次 (n_1+n_2+...+n_k=n)

因此,对这样的 n 个元素进行全排列,可得到不同的排列个数实际上为:\frac{n!}{n_1!n_2!...n_k!}

若只对其中的 r 个元素进行全排列,就用到了指数型母函数:

构造母函数:G(x)=(1+\frac{x}{1!}+\frac{x^2}{2!}+...+\frac{x_k_1}{k_1!})(1+\frac{x}{1!}+\frac{x^2}{2!}+...+\frac{x_k_2}{k_2!})...(1+\frac{x}{1!}+\frac{x^2}{2!}+...+\frac{x_k_n}{k_n!})

化简得:G(x)=a_0+a_1x+\frac{a_2}{2!}x^2+\frac{a_3}{3!}x^3+...+\frac{a_p}{p!}x^p,其中 p=k_1+k_2+...+k_na_i 为选出 i 个物品的排列方法数

注:若题中有限定条件,只需将第 i 项出现的列在第 i 项的式子中,未出现的不用列入

例如:物品 i 出现的次数为非 0 偶数,则原式改为:...*(\frac{x^2}{2!}+\frac{x^4}{4!}+...+\frac{x^{ki}}{k_i!})*...

3.实现

double num[15];//第i个物品有num[i]个
double a[15],b[15];
double fac(int n) { //求阶乘
    double ans=1.0;
    for(int i=1; i<=n; i++)
        ans*=i;
    return ans;
}
int main() {
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF) {
        for(int i=1; i<=n; i++)
            cin>>num[i];

        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        
        for(int i=0; i<=num[1]; i++)//a[0]=1.0;
            a[i]=1.0/fac(i);

        for(int i=2; i<=n; i++) {
            for(int j=0; j<=m; j++) {
                for(int k=0; k<=num[i]&&j+k<=m; k++) {
                    b[j+k]+=a[j]/fac(k);
                }
            }
            for(int j=0; j<=m; j++) {
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%.0lf\n",a[m]*fac(m));
    }
    return 0;
}

【例题】

  • Square Coins(HDU-1398)(n1为0,n2为无穷下的普通母函数):点击这里
  • Holding Bin-Laden Captive! (HDU-1085)(n1为0,忽视p的普通母函数):点击这里
  • 排列组合(HDU-1521)(指数型母函数):点击这里

你可能感兴趣的:(—————组合数学—————,#,组合数学——母函数)