母函数及其应用


一、母函数的定义

对于序列a0,a1,a2… 构造一函数:
G(x) = a0 + a1x + a2x^2 + …,
称函数G(x)是序列a0,a1,a2,…的母函数。
如果已知函数:G(x) = (1+x)^2
如果我们可以生成一个序列1,2,1,所以:G(x)是1,2,1的母函数。

问题引入:
若有1,2,3,4克砝码各一枚,能称出哪几种重量?各有几种可能方案?
构造母函数:用x的支书表示出称出的重量,则
1个1g的砝码可以用函数1+x表示;
1个2g的砝码可以用函数1+x^2表示;2个2g用函数1+x^2+x^4
1个3g的砝码可以用函数1+x^3表示;
1个4g的砝码可以用函数1+x^4表示;
(1+x)*(1+x^2)*(1+x^3)*(1+x^4) = 1+x+x^2+2x^3+2x^4…
系数是方案数,而指数是重量。
3个1g的砝码,1+x+x^2+x^3

概念:整数拆分,就是把整数拆分成若干整数的和。
拆分成若干整数的和,办法不已,不同拆分法的总数叫做拆分数

二、普通型母函数-模板(整数拆分为例)

练习:整数n拆分成1,2,3,4,…,m的和,求母函数。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <bits/stdc++.h>
using namespace std;
const int lmax = 10000;
int c1[lmax+1],c2[lmax+1];
// 母函数 g = (1+x+x^2+...)*(1+x^2+x^4+...)(1+x^3+x^6+...)...
int main()
{
    int n,i,j,k;
    while(cin>>n)
    {
        for(i=0; i<=n; i++) // 初始化
        {
            c1[i]=0;
            c2[i]=0;
        }
       for(i=0; i<=n; i++) //初始化第一个小括号里面的表达式系数,下标表示指数
            c1[i]=1;
        for(i=2; i<=n; i++) // 控制进行几次,两对括号的合并。几个括号一般确定
        {
            for(j=0; j<=n; j++) //第一个括号 
            {  // 下面的k的变化可以用数组使其加一定的值给于变化。
                for(k=0; k+j<=n; k+=i)//i表示第二个括号指数的增值(没有表示)
                                      // c2表示两个括号称的结果,
                {
                    c2[j+k]+=c1[j]*1; // 因为第二个数组的系数是1,所以*1
                }
            }
            for(j=0; j<=n; j++)
            {
                c1[j]=c2[j];
                c2[j]=0;
            }
        }
        cout  << c1[n] << endl;
    }
    return 0;
}

三、例题

1、Square Coins

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
using namespace std;
int a[401],b[401];
int main()
{
    int n,i,j,k,l,summax;
    while(~scanf("%d",&n)&&n)
    {
        for(i=0;i<=300;i++)
        {
            a[i]=1;
            b[i]=0;
        }
        for(i=2;i<=17;i++)
        {
            for(j=0;j<=n;j++)
            {
                for(k=0;k<=n-j;k+=i*i)
                    b[k+j]+=a[j];
            }
            for(j=0;j<=n;j++)
            {
                a[j]=b[j];
                b[j]=0;
            }
        }
        printf("%d\n",a[n]);
    }
}

2、Holding Bin-Laden Captive!

#include<iostream>
#include<cstring>

using namespace std;

const int maxn=10000;

int c1[maxn],c2[maxn],t[6];

int main()
{
    while(scanf("%d%d%d",&t[1],&t[2],&t[5])){
        if(t[1]==0&&t[2]==0&&t[5]==0){
            break;
        }else{
            int sum=t[1]+t[2]*2+t[5]*5;
            for(int i=0;i<=sum;i++){
                c1[i]=0;
                c2[i]=0;
            }
            for(int i=0;i<=t[1];i++){
                c1[i]=1;
            }
            for(int i=0;i<=t[1];i++){
                for(int j=0;j<=t[2]*2;j+=2){
                    c2[j+i]+=c1[i];
                }
            }
            for(int i=0;i<=t[1]+t[2]*2;i++){
                c1[i]=c2[i];
                c2[i]=0;
            }
            for(int i=0;i<=t[1]+t[2]*2;i++){
                for(int j=0;j<=t[5]*5;j+=5){
                    c2[j+i]+=c1[i];
                }
            }
            for(int i=0;i<=t[1]+t[2]*2+t[5]*5;i++){
                c1[i]=c2[i];
                c2[i]=0;
            }
            int i;
            for(i=0;i<=sum;i++){
                if(!c1[i]){
                    cout<<i<<endl;
                    break;
                }
            }
            if(i==sum+1){
                cout<<i<<endl;
            }
        }
    }
    return 0;
}

3、Big Event in HDU

#include<cstdio>
#include<stack>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<map>
#include<iostream>
using namespace std;
int c[250002];
int main()
{
    int n;
   while(scanf("%d",&n)!=EOF)
    {
       if(n<0)
           break;
       int time[55],num[55];
       int sum=0;
       for(int i=0;i<n;i++)
       {
           scanf("%d%d",&time[i],&num[i]);
           sum+=time[i]*num[i];
       }
       for(int i=0;i<=sum/2;i++)
       {
           if(i%time[0])
               c[i]=0;
           else
               c[i]=1;
       }
       for(int i=0;i<n;i++)
       {
           for(int j=0;j<=sum/2;j++)
           {
               for(int k=0;k+j<=sum/2&&k<=time[i]*num[i];k+=time[i])
               {
                   if(c[j]==1)
                       c[j+k]=1;
               }
           }
       }
       int i;
       for(i=sum/2;i>=0;i--)
       {
           if(c[i]!=0)
               break;
       }
       printf("%d %d\n",sum-i,i);
    }
}


4、The Balance

    #include <iostream>  
    #include <cstring>  
    #include <cmath>  
    using namespace std;  
    #define maxn 101   


    int a[maxn*maxn],b[maxn*maxn],c[maxn];  
    int main()  
    {  
        int n;  
        while(cin>>n)  
        {  
            int sum = 0;  
            for(int i = 0; i < n; i++)  
            {  
                cin>>c[i];  
                sum+=c[i];  
            }  
            memset(a,0,sizeof(a));  
            memset(b,0,sizeof(b));  
            for(int i = 0; i <= c[0]; i+=c[0])  
                a[i] = 1;  
            //int now = c[0];  
            for(int i = 1; i < n; i++)  //i代表第i个表达式   
            {  
                for(int j = 0; j <= sum; j++)  //j表示每个表达式的第i项   
                {  
                    for(int k = 0; k <= c[i]; k+=c[i])  //k代表每个表达式第i项的指数   
                    {  
                        b[j+k] += a[j];  
                        b[abs(j-k)] +=a[j];  
                    }  
                }  
                //now += c[i];  
                for(int j = 0; j <= sum; j++)  
                {  
                    a[j] = b[j];  
                    b[j] = 0;  
                }  
            }  
            int num = 0;  
            for(int i = 0; i <= sum; i++ )  
            {  
                if(!a[i])  
                    num++;  
            }  
            cout<<num<<endl;  
            int p = 1;  
            if(num){  
                for(int i = 0; i <= sum; i++)  
                {  
                    if(!a[i])  
                    {  
                        if(!p)  
                        cout<<" "<<i;  
                        else  
                        {  
                            cout<<i;  
                            p = 0;  
                        }      
                    }  

                }  
                cout<<endl;  
            }  

        }  
    }  

5、fruit

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int mi[105],mx[105],c1[105],c2[105];

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

6、排列组合

有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。

#include <iostream>

#include<stdio.h>

#include<memory.h>

using namespace std;

int f(int n)

{
    int i,s=1;
    for(i=1; i<=n; i++)
        s=s*i;
    return s;
}
int main()
{
    int n,m,i,j,k,num[12];
    double c1[12],c2[12];//因为用到了除法,所以用double型来存储。
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        for(i=0; i<n; i++)
        {
            cin>>num[i];
        }
        for(i=0; i<=num[0]; i++)//赋初值
            c1[i]=1.0/f(i)
        for(i=1; i<n; i++)//n个括号
        {
            for(j=0; j<=n; j++)
                for(k=0; k<=num[i]&&k+j<=n; k++)
                    c2[k+j]+=c1[j]/f(k);
            for(j=0; j<=n; j++)
            {
                c1[j]=c2[j];
                c2[j]=0;
            }
        }
        printf("%.lf\n",c1[m]*f(m));
    }

    return 0;

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值