母函数(生成函数)

母函数入门

 

一、定义介绍:

母函数,又称生成函数,是ACM竞赛中经常使用的一种解题算法,常用来解决组合方面的题目。

在数学中,某个序列的母函数(Generating function,又称生成函数)是一种形式幂级数,其每一项的系数可以提供

关于这个序列的信息。使用母函数解决问题的方法称为母函数方法。

母函数可分为很多种,包括普通母函数、指数母函数、L级数、贝尔级数和狄利克雷级数。

对每个序列都可以写出以上每个类型的一个母函数。构造母函数的目的一般是为了解决某个特定的问题,因此选用何种母函数视乎序列本身的特性和问题的类型。


这里先给出两句话

1.“把组合问题的加法法则和幂级数的乘幂对应起来”

2.“母函数的思想很简单 ——就是把离散数列和幂级数一一对应起来,把离散数列间的相互结合关系对应成为幂级数间的

    运算关系,最后由幂级数形式来确定离散数列的构造. ”

 

母函数用一维数组就可以表示: a[ ]            下标表示组成的数量,值表示方法数


我们首先来看下这个多项式乘法:

 

由此可以看出:

1.x的系数是a1,a2,…an 的单个组合的全体。

2. x^2的系数是a1,a2,…a2的两个组合的全体。

………

n. x^n的系数是a1,a2,….an的n个组合的全体(只有1个)。

 

进一步得到:

 

母函数的定义

对于序列a0,a1,a2,…构造一函数:

称函数G(x)是序列a0,a1,a2,…的母函数。

 

、问题引入


1、第一种例题:

  • 假设我们当前有质量分别为 1g, 2g, 3g, 4g 的砝码 各一个 , 我们可以构造出多少种质量为7g的砝码方案?

    基础解法

  • 显然, 想要指定重量的砝码序列的构造, 对于每一个砝码, 我们都有使用或者不使用两种选择. 如果我们对每个砝码进行枚举的话, 则共需要枚举 2^4 种选择, 即这种解法的时间复杂度为 O(2^n) .

    代数解法

  • 考虑用母函数来解决这个问题:

    我们假设x表示砝码,x的指数表示砝码的重量,这样:

    1个1克的砝码可以用函数1+1*x^1表示,

    1个2克的砝码可以用函数1+1*x^2表示,

    1个3克的砝码可以用函数1+1*x^3表示,

    1个4克的砝码可以用函数1+1*x^4表示,

    上面这四个式子懂吗?

    我们拿1+x^2来说,前面已经说过,x表示砝码,x的指数表示砝码的重量!初始状态时,这里就是一个质量为2的砝码。

    那么前面的1表示什么?按照上面的理解,1其实应该写为:1*x^0,即1代表重量为2的砝码数量为0个。

    所以这里1+1*x^2 = 1*x^0 + 1*x^2,即表示2克的砝码有两种状态,不取或取,不取则为1*x^0,取则为1*x^2

     

    不知道大家理解没,我们这里结合前面那句话:

    “把组合问题的加法法则和幂级数的乘幂对应起来“

     

    接着讨论上面的1+x^2,这里x前面的系数有什么意义?

    这里的系数表示状态数(方案数)   ( 幂级数前边的系数表示的是  方案数)

    1+x^2,也就是1*x^0 + 1*x^2,

  • 也就是上面说的不取2克砝码,此时有1种状态;或者取2克砝码,此时也有1种状态。(分析!)

     

所以,前面说的那句话的意义大家可以理解了吧?

几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:

G(x)=                                      // 母函数表示的是 每克只有一个,取或不取

  • (1+x)(1+x^2)(1+x^3)(1+x^4)                  

  • =(1+x+x^2+x^4)(1+x^3+^4+x^7)

  • =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

 

从上面的函数知道:可称出从1克到10克,系数便是方案数。(!!!经典!!!)

 

例如右端有2^x^5 项,即称出5克的方案有2种:5=3+2=4+1;同样,6=1+2+3=4+2;10=1+2+3+4。

故称出6克的方案数有2种,称出10克的方案数有1种 。


2、第二种例题:

求用1分、2分、3分的邮票贴出不同数值的方案数:

大家把这种情况和第一种比较有何区别?第一种每种是一个,而这里每种是无限的。

 

母函数表示的是每一个分额的邮票可以表示的数值

例如第一个括号: 1 分的可以表示 1分,2分,3分,4分,5分,6分......

第二个括号: 2 分的可以表示 2分,4分,6分,8分,10分......

第三个括号: 3 分的可以表示 3分,6分,9分,12分,15分......

以展开后的x^4为例,其系数为4,即4拆分成1、2、3之和的拆分方案数为4;

即 :4=1+1+1+1=1+1+2=1+3=2+2


母函数通常解决类似如下的问题:

给5张1元,4张2元,3张5元,要得到15元,有多少种组合?

某些时候会规定至少使用3张1元、1张2元、0张5元。

某些时候会规定有无数张1元、2元、5元

……

三、解题过程

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

如(1+x+x^2)(1+x^4+x^8)(x^5+x^10+x^15)

通用表达式为

(x^(v[0]*n1[0])+x^(v[0]*(n1[0]+1))+x^(v[0]*(n1[0]+2))+...+x^(v[0]*n2[0]))
(x^(v[1]*n1[1])+x^(v[1]*(n1[1]+1))+x^(v[1]*(n1[1]+2))+...+x^(v[1]*n2[1]))
...
(x^(v[K]*n1[K])+x^(v[K]*(n1[K]+1))+x^(v[K]*(n1[K]+2))+...+x^(v[K]*n2[K]))

K  对应具体问题中物品的种类数。

v[i] 表示该乘积表达式第 i 个因子的权重,对应于具体问题的每个物品的价值或者权重。

n1[i] 表示该乘积表达式第i个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个。

n2[i] 表示该乘积表达式第i个因子的终止系数,对应于具体问题中的每个物品的最多个数,即最多要取多少个。

对于表达式(1+x+x^2)(x^8+x^10)(x^5+x^10+x^15+x^20)

v[3]={1,2,5},n1[3]={0,4,1},n2[3]={2,5,4}。

 

解题的关键是要确定v、n1、n2数组的值。

通常n1都为0,但有时候不是这样。

n2有时候是无限大。

四、CODE :

// 本题是 HDU 1398  的代码,所以循环中的 17 只针对本题,具体题目具体分析

一般可以改成

for (int i = 2; i <= n; i++)
{
    for (int j = 0; j <= n; j++)
    {
        for (int k = 0; k + j <= n; k += i)
            b[k + j] += a[j];
    }
}

 



    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int MAXN = 300 + 10;
    int a[MAXN];
    int b[MAXN];
    int main()
    {
        int n;
        while(cin >> n && n)
        {
            for(int i = 0;i <= 300;i++) // 初始化  ————————————1
            {
                a[i] = 1; // 模拟第一个括号,各项的系数为1
                b[i] = 0; // 中间数组
            }
            for(int i = 2;i <= 17;++i) // 外层括号数,17项母函数——————2
            {
                for(int j = 0;j <= n;++j) // 合并前两个式子,这里前两个式子代表每次交换后的两个————————3
                {
                    for(int k = 0;k + j <= n;k += i * i)// 增量——————————4
                    {
                        b[k + j] += a[j]; // 将相同次幂的系数合到一起
                    }
                }
                for(int j = 0;j <= n;++j) // 清空C 继续接受下一个式子————————————5
                {
                    a[j] = b[j];
                    b[j] = 0;
                }
            }
            cout << a[n] << endl;
        }
        return 0;
    }

解释代码:

①  、首先对c1初始化,由第一个表达式(1+x+x^2+..x^n)初始化,把质量从0到n的所有砝码都初始化为1.

②  、 i从2到n遍历,这里   i   就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。

③、j 从0到n遍历,这里j就是(前面几个表达式累乘的表达式)里第j个变量,如  (1+x)(1+x^2)(1+x^3),j 先指示的是1和x的系数,           i=2执行完之后变为 (1+x+x^2+x^3)(1+x^3),这时候  j   应该指示的是合并后的第一个括号的四个变量的系数。

④ 、 k 表示的是第 j 个指数,所以 k 每次增 i(因为第i个表达式的增量是i)

        例如第二种方法中的第二个括号  (1+x^4+x^8)   指数是以 2增加的 ( 针对本题)

⑤  、把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的。

17个括号,先前两个结合成为一个,再与第三个结合,一次进行下去

附上该题来源

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值