母函数模板解释

母函数模板
1;母函数应用于——————形式上说,普通型生成函数用于解决多重集的组合问题,而指数型母函数用于解决多重集的排列问题.。现在我们先讨论普通生成函数;

2;定义;
(1+x)^n = 1 + C(n,1)x +C(n,2)x^2 + C(n,3)x^3+~~~~~~~~~+C(n,n)^n;
==> G(x)=a0 + a1x + a2x^2 + ~~~~~ + anx^n;

函数G(X)就称序列a0,a1,a2,a3~~~~~an的母函数;
看一个经典的例子;

例一;
有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?
考虑用母函数来解决这个问题:

我们假设x表示砝码,x的指数表示砝码的重量,这样:
1个1克的砝码可以用函数1+x表示,
1个2克的砝码可以用函数1+x∧2表示,
1个3克的砝码可以用函数1+x∧3表示,
1个4克的砝码可以用函数1+x∧4表示

解释;以1+x∧2为例;
2克的砝码有两种状态,取or不取; 1*x^0+1*x^2;就是1+1*x^2;
看联系;G(x) = (1+x^1)(1+x^2)(1+x^3)*(1+x^4);每个括号内都表示每种砝码的情况。
=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;

从最后面的式子可以知道;可以称出1g到10g,前面的系数就是方案数。指数表示可以称出的重量。
列如5g就找到x系数为5的式子;2*x^5系数为2则有2种方案可以称出5g;

例二;
用1分,2分,3分的邮票可以贴出数值的方案数。
该例子对比例一的区别,这里是无限的每种邮票都可以;1~无限。
=======G(x)=(1+x+x^2+x^3+……)(1+x^2+x^4+x^6……)(1+x^3+x^6+x^9……)+( ……);
然后就是同样的,
拆开后;前面的系数就是方案数。指数表示可以称出的重量。

对于这种母函数的应用应该懂了吧。
个人理解;就是一句话,把每种列子的每种情况列出相乘就是要求的母函数了,母函数拆开之后就是结果了,幂表示情况,前面的系数表示该种情况下的方案种类;

现在我们考虑的是怎么用代码实现例二的情况呢。就是说我要怎么用用代码实现,输入一个数可以输出这种重量的方案数。
一位大牛说,我们怎么笔算的就用这种思维去实现,然而……还是不会。

先说一下核心思路吧。
**1;要开两个数组,c1,c2;
C1;保存当前得到的多项式各式系数;
C2;保存每次计算是的临时结果;
并且在之后每计算一次完毕之后就要把c2赋值给c1,并且将c2初始化为0;
2;三重循环
第一重;记录它正在与第几个多项式相乘。
第二重;表示c1中每一项;
第三重;被后面相乘的每一项**。

在根据这些去落实代码吧;

while(scanf("%d",&n) != EOF){
        for(i = 0; i <= n; i++){//初始化;要注意并不是每次都是这样初始化的。
            c1[i] = 1;
            c2[i] = 0;
        }
        for(i = 2; i <= n; i++){
            for(j = 0; j <= n; j++){
                for(k = 0; k+j <= n; k += i){ //这里的k+=i表示的是表达式i中幂的变化规律。刚好是加i;注意适当变化。
                    c2[k+j] += c1[j];
                }
            }
            for(j = 0; j <= n; j++){
                c1[j] = c2[j];
                c2[j] = 0;
            }
        }
        printf("%d\n",c1[n]);
    }
;

分析代码;
1;

for(i = 0; i <= n; i++){//初始化;要注意并不是每次都是这样初始化的。
            c1[i] = 1;
            c2[i] = 0;
        }

初始化;这样初始化的条件是每项都有从1到n克都存在的情况下,不能存在中间有一项没有这种情况。
列如例子2082就是不能这样的,他中间存在没有的情况。

2;三重循环;

for(i = 2; i <= n; i++){//表示第i个表达式;
            for(j = 0; j <= n; j++){//表示、c1中的每一项。结果是幂;
                for(k = 0; k+j <= n; k += i){ //表示与第i个表达式中的幂为k的项相乘;
                    c2[k+j] += c1[j];//这里是累加。

看另外一种模板;代码出自于杭电2082;

memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0]=1;//有些是0,则不能套用模板中的都等于1;
        //c1[0]=1;相当于入口、 
        for(i = 1; i <= 26; i++){ 
            for(j = 0; j <= 50; j++){//c1的各项的指数
                for(k = 0; k <= x[i] && j+k*i <= 50; k++){ 
                    c2[j+k*i] += c1[j];
                }//这里的k表示表达式中的第k项,并且该项的幂是k*i;  
            }
            for(j = 0; j <= 50; j++){
                c1[j] = c2[j];
                c2[j] = 0;
            }
        }

对比一下上个代码;
区别;
1;初始化那里不同,这里只是c1[0]=1;而不是全部初始化为1;因此这样的初始化适用范围更广,可以是连续的情况
2;第三重循环那里不同,不再是k+=I;而是k++了。然而这里的区别很大,。
这里的k表示的是第i个表达式中的第k项,并且该项的幂是k*i**;
前面那个代码k表示的是与第i个表达式中的幂为k的项相乘。
一个表示k项一个表示幂为k,这里注意区分

模板介绍完了,应该对母函数这一概念和应用有了一定了解了吧?

来套模板刷几道水题吧。
 现在HDOJ_1028Ignatius and the Princess III;
http://acm.hdu.edu.cn/showproblem.php?pid=1028
就是整数的拆分;
就是跟模板一模一样,直接上代码吧;

#include<stdio.h>
int c1[10000],c2[10000];


int main()
{
    int i, j, k,n;
    while(scanf("%d",&n) != EOF){
        for(i = 0; i <= n; i++){
            c1[i] = 1;
            c2[i] = 0;
        }
        for(i = 2; i <= n; i++){
            for(j = 0; j <= n; j++){
                for(k = 0; k+j <= n; k += i){
                    c2[k+j] += c1[j];
                }
            }
            for(j = 0; j <= n; j++){
                c1[j] = c2[j];
                c2[j] = 0;
            }
        }
        printf("%d\n",c1[n]);
    }    

    return 0 ;
}

继续吧;
HDOJ_1398 Square Coins
http://acm.hdu.edu.cn/showproblem.php?pid=1398
也是母函数题;注意题目的条件适当变化一下就ok了

#include<stdio.h>
int c1[305],c2[305];
int main()
{
    int n, i, j, k;
    while(scanf("%d",&n) != EOF && n != 0){
        for(i = 0; i <= n; i++){
            c1[i] = 1;
            c2[i] = 0;
        }
        for(i = 2; i <= 17; i++){
            for(j = 0; j <= n; j++){
                for(k = 0; k+j <= n; k+=i*i){
                    c2[j+k] += c1[j];
                }
            }
            for(j = 0; j <= n; j++){
                c1[j] = c2[j];
                c2[j] = 0;
            }
        }
        printf("%d\n",c1[n]);
    }
    return 0 ;
}

看到变化没,就是把第一重循环那里变成从2到17;因为最大就是2^17;
还有就是第三重循环后面变成了k+=i*i;因为第i个表达式的幂变化的i*i变化的,题目中说了,正方形哒。

继续来一道,要用后面那个模板的题吧。
Hdu2082找单词
http://acm.hdu.edu.cn/showproblem.php?pid=2082
这题是不是用模板一不行了。出现了可以不存在的情况,还有的是有界的并不是从0到无限,范围是要输入的,不多说,应该可以直接上代码了。上面那个模板就是根据这题打的。

#include<stdio.h>
#include<string.h>
int x[27];
int c1[55],c2[55];
int main()
{
    int n, i,j,k,count=0;
    scanf("%d",&n);
    while(n--){
        count = 0;
        for(i = 1; i <= 26; i++)
            scanf("%d",&x[i]);
        memset(c1, 0, sizeof(c1));
        memset(c2, 0, sizeof(c2));
        c1[0]=1;//有些是0,则不能套用模板中的都等于1;
        // c1[0]=1;相当于入口、 
        for(i = 1; i <= 26; i++){ 
            for(j = 0; j <= 50; j++){//c1的各项的指数
                for(k = 0; k <= x[i] && j+k*i <= 50; k++){//这里也有些不同。 
                    c2[j+k*i] += c1[j];
                }//这里的k表示表达式中的第k项,并且该项的幂是k*i; 

            }
            for(j = 0; j <= 50; j++){
                c1[j] = c2[j];
                c2[j] = 0;
            }
        }
        for(i = 1; i <= 50; i++){
            count += c1[i];
        }
        printf("%d\n",count);
    }

    return 0 ;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值