母函数学习及例题讲解

母函数学习及例题讲解


母函数,是组合数学中尤其是计数方面 的一个重要理论和工具,运用这种数学 方法往往对程序效率与速度有很大改进。(百度百科)

  简而言之,母函数在组合数学中运用了十分巧妙的方法简化了问题,或者说找到了问题的等价映射,这个时候就要列出是谁发现了这么个玩意,因为学了下面的你会知道母函数是何等的有用与神奇。

1812年,法国数学家拉普拉斯在著作《概率的分析理 论》的第一卷中系统地研究了母函数方法及与之有关 的理论
(清华大学 马昱春老师课件)

好了,知道是谁发明的了,那么我们直接来看原理。
母函数的原理实际上就是将不同的情况分类并用次方的形式表示出来。
比如说,我要从水果店里挑四个水果,现在已知水果店里有苹果、香蕉、梨子、橙子四种水果,数量均足够,请问有多少种情况。这个例子的母函数就是

Gx=(1+x+x 2 +x 3 +x 4 )(1+x+x 2 +x 3 +x 4 )(1+x+x 2 +x 3 +x 4 )(1+x+x 2 +x 3 +x 4 ) 

这里面每一个小项所表示的意思都是不一样的,比如第一个表示的是苹果的情况,1代表没有苹果及 x 0   ,之后x表示有一个苹果, x 2   表示有两个苹果,以此类推。
那么我们不难发现,在这种情况下加法及代表了高中数学中的分类加法原理、乘法及代表了分步乘法原理,那么很显然的这个多项式列出了所述事件的所有情况,那么将这个多项式打开找到打开后 x 4   的那一项的系数,及我们想求的那个情况的种数。
好的,那么熟知了大致的原理,我们直接放题;

Problem Description
假设有x1个字母A, x2个字母B,….. x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,….. 字母Z的价值为26。那么,对于给定的字母,可以找到多少价值<=50的单词呢?单词的价值就是组成一个单词的所有字母的价值之和,比如,单词ACM的价值是1+3+14=18,单词HDU的价值是8+4+21=33。(组成的单词与排列顺序无关,比如ACM与CMA认为是同一个单词)。

Input
输入首先是一个整数N,代表测试实例的个数。
然后包括N行数据,每行包括26个<=20的整数x1,x2,…..x26.

Output
对于每个测试实例,请输出能找到的总价值<=50的单词数,每个实例的出占一行。

Sample Input
2
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 2 6 22 7 0 2 2 7 5 10 6 10 2 10 6

Sample Output
7
379297

好的 先附上ac代码

#include

int main(void)
{
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int c1[51] = { 0 }, c2[51] = { 0 };
        int c3[27] = {0};
        for (int i = 1; i <= 26; i++)
        {
            scanf("%d", &c3[i]);
        }
        for (int i = 0; i <= c3[1]; i++)
        {
            c1[i] = 1;
        }
        for (int i = 2;i <= 26;i++)
        {
            for (int j = 0;j <= 50;j++)
                for (int k = 0;k + j <= 50&&k<=c3[i]*i;k += i)
                {
                    c2[j + k] += c1[j];
                }
            for (int j = 0;j <= 50;j++)
            {
                c1[j] = c2[j];  c2[j] = 0;
            }
        }
        int count=0;
        for (int i = 1; i <=50; i++)
        {
            if (c1[i]) {
                count += c1[i];
            }

        }
        printf("%d\n", count);
    }
    return 0;
}

好了,个人感觉这一题属于那一种带限制条件的母函数的问题,c3[]数组表示的是限制条件,思路上面已经说得差不多了,那么我们来看核心代码。

for (int i = 2;i <= 26;i++)
        {
            for (int j = 0;j <= 50;j++)
                for (int k = 0;k + j <= 50&&k<=c3[i]*i;k += i)
                {
                    c2[j + k] += c1[j];
                }
            for (int j = 0;j <= 50;j++)
            {
                c1[j] = c2[j];  c2[j] = 0;

}
        }

    
 至于数组初始化的问题,讲完了核心代码之后就应该懂了该如何去初始化,我们来看这个循环,首先i从2初始化,代表着小项从第二项开始乘,很简单的道理,因为第一项不能乘自己,所以只能从第二项开始才行,那么接着往下看,下面是一个双层的for循环,其中第一个表示大项,至于什么是大项,接着往下看就知道了,那么第二个表示乘的那一项,如果有些迷惑我们看具体的例子。  
       及第一次循环,代表第二个括号里跟第一个括号里相乘,因为第二个括号里的系数全部为1,所以乘之后的系数就等于与之乘的那个第一个括号里的数的系数。                

c2[j + k] += c1[j];

       这一段的意思就是这样,那么接着k+=i及是第二个括号的第一项变到第二项,这个要依据具体题目来定,本题就是k+=i。接下来就是第一个括号的第一项和第二个括号的第二项相乘,直到里层for循环结束。                                                                           
        然后j++表示第一个括号的第二项再乘第二个括号的各项,依次类推。做完第一个括号与第二个括号的乘法运算后,将c2[]赋值给c1[],这一步的意思就是说,两个括号乘完之后就变成了一个大的括号,也就是上文所说的大项,再用这个大项去跟第三个括号的式子去相乘。                                         
        基本原理就是这样,本题有数目作为第二个限制条件(第一个是不能超过50)。    
         关于母函数就到这里,小白刷题,欢迎大佬指正。
  

你可能感兴趣的:(acm算法)