• ACM数论之旅16---母函数(又名生成函数)(痛并快乐着(╭ ̄3 ̄)╭)


    (前排出售零食瓜子)

    前言:

    母函数是个很难的东西,难在数学

    而ACM中所用的母函数只是母函数的基础

    应该说除了不好理解外,其他都是非常简单的

    母函数生成函数,是组合数学中尤其是计数方面的一个重要理论和工具。

    但是ACM中的母函数木有像数学那么深究,应用的都是母函数的一些基本

    (就好比方程的配方,因式的分解,写起来容易,你用电脑写起来就麻烦了,所以学计算机就不要老跟数学家瞎闹( ̄3 ̄))

    什么是母函数

    就是把一个已知的序列和x的多项式合并起来,新产生的多项式就叫原来序列的母函数

    至于怎么合并,看这个例子

    序列{0,1,2,3,4,5...n}的母函数就是

    f(x)=0+x+2x^2+3x^3+4x^4+...+nx^n(这个x没有任何意义,应该说,你不需要把它当做一个函数,你只要知道母函数这么写就可以了)

    序列{1,1,1,1,1......}的母函数就是

    f(x)=1+x+x^2+x^3+x^4....

    二项式展开的序列比如这个{1,4,6,4,1,0,0,0,0,0.....}是C(4,0)到C(4,4)的系数,那它的母函数就是

    f(x)=1+4x+6x^2+4x^3+1x^4

    母函数就长这样,对正常人来讲,这种东西毫无意义( ° △ °|||)

    那看点有意义的东西(以下都是经典题型,我从杭电ACM课件抄来的)

    有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?

    假如x的幂次数表示几克的砝码

    那么

    1克的砝码表示为1+x^1

    2克的砝码表示为1+x^2

    3克的砝码表示为1+x^3

    4克的砝码表示为1+x^4

    每个砝码都可以选择取或不取

    所以这里的1可以认为1*x^0,表示不取这颗砝码

    那么把这些乘起来

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

    =1+(x^1)+(x^2)+2(x^3)+2(x^4)+2(x^5)+2(x^6)+2(x^7)+(x^8)+(x^9)+(x^10)

    根据指数来看,我们可以称出0~10这么多的重量,其中3~7的系数为2,说明有2种称的方法

    那么我们来细看一遍

    0:(什么砝码都不放).......................(1种)

    1:1.............................................(1种)

    2:2.............................................(1种)

    3:3或1+2.....................................(2种)

    4:4或1+3.....................................(2种)

    5:1+4或2+3.................................(2种)

    6:2+4或1+2+3..............................(2种)

    7:3+4或1+2+4..............................(2种)

    8:1+3+4......................................(1种)

    9:2+3+4......................................(1种)

    10:1+2+3+4.................................(1种)

    分毫不差(・ˍ・*)

    所以说母函数在ACM就是这么用的,跟函数没关系,跟写法有关系。。。

    再来一题

    求用1分、2分、3分的邮票贴出不同数值的方案数:(每张邮票的数量是无限的)

    那么

    1分:(1+x^1+x^2+x^3+x^4+......)

    2分:(1+x^2+x^4+x^6+x^8+......)

    3分:(1+x^3+x^6+x^9+x^12+......)

    然后这3个乘起来(让电脑去乘吧)

    对于这种无限的,题目肯定会给你他询问的数值的范围,计算到最大的范围就可以了

    附代码:

     1 #include<cstdio>
     2 typedef long long LL;
     3 const int N = 100 + 5;//假如题目只问到100为止 
     4 const int MAX = 3;//题目只有1,2,3这3种邮票 
     5 LL c1[N], c2[N];//c2是临时合并的多项式,c1是最终合并的多项式 
     6 int n;
     7 void init(){
     8     c1[0] = 1;//一开始0的情况算一种 
     9     for(int i = 1; i <= MAX; i ++){//把1分到MAXN的邮票合并,变成一个多项式 
    10         for(int j = 0; j < N; j += i){//i分的邮票,步长是i
    11             for(int k = 0; j + k < N; k ++){//从x^0到x^N遍历一遍 
    12                 c2[j + k] += c1[k];//因为j的所有项系数为1,所以c1[k]可以看成c1[k]*1; 
    13             }
    14         } 
    15         for(int j = 0; j < N; j ++){//把c2的数据抄到c1,清空c2 
    16             c1[j] = c2[j];
    17             c2[j] = 0;
    18         }
    19     }
    20 } 
    21 int main(){
    22     init();
    23     while(scanf("%d", &n) != EOF){
    24         printf("%I64d
    ", c1[n]);
    25     }
    26 }

    我们就来把这个模板用于实际吧

    hdu 1028

    http://acm.hdu.edu.cn/showproblem.php?pid=1028

    题目问一个数字n能够拆成多少种数字的和

    比如n=4

      4 = 4;
      4 = 3 + 1;
      4 = 2 + 2;
      4 = 2 + 1 + 1;
      4 = 1 + 1 + 1 + 1;

    有5种,那么答案就是5

    AC代码:

     1 #include<cstdio>
     2 typedef long long LL;
     3 const int N = 120 + 5;
     4 const int MAX = 120 + 5;
     5 LL c1[N], c2[N];
     6 int n;
     7 void init(){
     8     c1[0] = 1;
     9     for(int i = 1; i <= MAX; i ++){
    10         for(int j = 0; j < N; j += i){
    11             for(int k = 0; j + k < N; k ++){
    12                 c2[j + k] += c1[k];
    13             }
    14         } 
    15         for(int j = 0; j < N; j ++){
    16             c1[j] = c2[j];
    17             c2[j] = 0;
    18         }
    19     }
    20 } 
    21 int main(){
    22     init();
    23     while(scanf("%d", &n) != EOF){
    24         printf("%I64d
    ", c1[n]);
    25     }
    26 }
    View Code

    再来,hdu 1398

    http://acm.hdu.edu.cn/showproblem.php?pid=1398

    题目说一个国家的硬币都是方形的,面值也是方形的

    有1块钱,4块钱,9块钱,16块钱......一直到289块钱(17^2)

    问想组成n块钱有几种方法

    AC代码:

     1 #include<cstdio>
     2 typedef long long LL;
     3 const int N = 300 + 5;
     4 const int MAX = 17;
     5 LL c1[N], c2[N];
     6 int n;
     7 void init(){
     8     c1[0] = 1;
     9     for(int i = 1; i <= MAX; i ++){
    10         for(int j = 0; j < N; j += i*i){
    11             for(int k = 0; j + k < N; k ++){
    12                 c2[j + k] += c1[k];
    13             }
    14         } 
    15         for(int j = 0; j < N; j ++){
    16             c1[j] = c2[j];
    17             c2[j] = 0;
    18         }
    19     }
    20 } 
    21 int main(){
    22     init();
    23     while(scanf("%d", &n) != EOF && n){
    24         printf("%I64d
    ", c1[n]);
    25     }
    26 }
    View Code

    都是改一些小地方,都是模板题(o゚ω゚o)

    最后一道

    hdu 1085

    http://acm.hdu.edu.cn/showproblem.php?pid=1085

    AC代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 typedef long long LL;
     4 const int N = 1000 * (1+2+5) + 5;
     5 int cost[3] = {1, 2, 5};
     6 LL c1[N], c2[N];
     7 int num[3];
     8 int MAX;
     9 int main(){
    10     while(~scanf("%d%d%d", &num[0], &num[1], &num[2])){
    11         if(num[0] == 0 && num[1] == 0 && num[2] == 0) break;
    12         memset(c1, 0, sizeof(c1));
    13         memset(c2, 0, sizeof(c2));
    14         MAX = num[0] + num[1] * 2 + num[2] * 5;//计算最大值
    15         c1[0] = 1;
    16         for(int i = 0; i < 3; i ++){
    17             for(int j = 0; j <= num[i] * cost[i]; j += cost[i]){
    18                 for(int k = 0; j + k <= MAX; k ++){
    19                     c2[j + k] += c1[k];
    20                 }
    21             } 
    22             for(int j = 0; j < N; j ++){
    23                 c1[j] = c2[j];
    24                 c2[j] = 0;
    25             }
    26         }
    27         for(int i = 1; i <= MAX + 1; i ++){
    28             if(!c1[i]){
    29                 printf("%d
    ", i);
    30                 break;
    31             }
    32         }
    33     }
    34 } 
    View Code

    母函数在数学上真的用处很大,但是我没怎么看到在ACM上有什么太大的用处(可能我做的题还不够多 T_T)

    比如刚刚上面的3个例题,都有更快的做法

    第一题:动态规划,时间复杂度O(n^2)

     1 #include<cstdio>
     2 const int N = 120 + 5;
     3 int dp[N];
     4 int n;
     5 void init(){
     6     dp[0] = 1;
     7     for(int i = 1; i < N; i ++){
     8         for(int j = i; j < N; j ++){
     9             dp[j] += dp[j - i];
    10         }
    11     }
    12 }
    13 int main(){
    14     init();
    15     while(scanf("%d", &n) != EOF){
    16         printf("%d
    ", dp[n]);
    17     }
    18 }
    View Code

    第二题:动态规划,时间复杂度O(n^2)

     1 #include<cstdio>
     2 const int N = 300 + 5;
     3 int dp[N];
     4 int n;
     5 void init(){
     6     dp[0] = 1;
     7     for(int i = 1; i <= 17; i ++){
     8         for(int j = i*i; j < N; j ++){
     9             dp[j] += dp[j - i*i];
    10         }
    11     }
    12 }
    13 int main(){
    14     init();
    15     while(scanf("%d", &n) != EOF && n){
    16         printf("%d
    ", dp[n]);
    17     }
    18 }
    View Code

    第三题:≖‿≖✧特判就好了,时间复杂度O(1)

     1 #include<cstdio>
     2 int a, b, c;
     3 int ans;
     4 int main(){
     5     while(~scanf("%d%d%d", &a, &b, &c) && (a || b || c)){
     6         if(a >= 4 || a >= 2 && b >= 1 || a >= 1 && b >= 2) ans = a + 2*b + 5*c + 1;
     7         else if(a == 0) ans = 1;
     8         else ans = a + 2*b + 1;
     9         printf("%d
    ", ans);
    10     }
    11 }
    View Code

    哈哈哈有没有被骗的感觉,有些题目,不要陷进算法里,这题O(1)的复杂度就可以了,如果你用三个for循环,那就太慢了,而且数量不同,还没有办法预处理,如果数据量大,肯定超时

    所以,母函数我们只要理解原理就好了

    那么ACM的母函数讲完了(*°∀°)

    之后是数学上的母函数,不想看的人就可以结束本章内容了(*°∀°)

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    哇哈哈╰(*°▽°*)╯我也不想讲了
    自己百度去吧╰(*°▽°*)╯
    据说可以求很多东西
    比如Fibonacci(斐波那契)数列的通项公式
    Catalan数(卡塔兰数)的通项公式也是母函数求得的
    还有"牛顿二项式定理"等等。。。
    想了解自己百度╰(*°▽°*)╯
  • 相关阅读:
    UVa 1331 最大面积最小的三角剖分
    UVa 1626 括号序列(矩阵连乘)
    POJ 3295 Tautology(构造法)
    POJ 2586 Y2K Accounting Bug(贪心)
    POJ 2109 Power of Cryptography
    abcd
    好数
    Gift
    密码游戏
    约瑟夫游戏
  • 原文地址:https://www.cnblogs.com/linyujun/p/5207730.html
Copyright © 2020-2023  润新知