母函数 基础

今天看了下母函数,纠结了好长时间,终于在和队友的讨论下自我感觉弄懂了。。。

母函数多用于处理排列组合问题,我们还是拿常见的举例子吧:

假如现在有面值为  2 3 5 的三种硬币各一枚,问能组成多少种不同的面值?每种面值的组合方式有多少种?

首先,我们用X表示硬币,X的指数表示硬币能表示的面值。那么,如果用函数表示每个硬币可以组成的面值,那么

面值为2的硬币可以表示的函数为 x^0+x^2;

面值为3的硬币可以表示的函数为 x^0+x^3;

面值为5的硬币可以表示的函数为 x^0+x^5;

即如果该硬币参与组成面值,那它能表示的面值为2,3,5,如果不参与,就相当于组成的面值为0;

把上面的多项式乘起来,我们会发现一点小规律:

(x^0+x^2)*(x^0+x^3)=x^0+x^2+x^3+x^5;

(x^0+x^2+x^3+x^5)*(x^0+x^5)=x^0+x^2+x^3+2*(x^5)+x^7+x^8+x^10;

我们发现:

2 3 5能组成面值为0的种数为1,即都不选

2 3 5能组成面值为2的种数为1,即选2

2 3 5能组成面值为3的种数为1,即选3

2 3 5能组成面值为5的种数为2,即选5或(2,3)

2 3 5能组成面值为7的种数为1,即选(2,5)

2 3 5能组成面值为8的种数为1,即选(3,5)

2 3 5能组成面值为10的种数为1,即选(2,3,5)

二项式的项数就是能组成的面值的种数,二项式  每项前面的系数就是 组成该项二项式指数的种数(二项式的指数表示能组成的面值)

 

上面介绍的是给定你每个面值的数量为1,现在假如三种面值的数量依次为3 7 4,那能每项能构成的函数是什么呢?

面值为2的硬币可以表示的函数为 x^0+x^2+x^4+x^6;

面值为3的硬币可以表示的函数为 x^0+x^3+x^6+x^9+x^12+x^15+x^18+x^21;

面值为5的硬币可以表示的函数为 x^0+x^5+x^10+x^15+x^20;

我们又会从上式中发现这样一个规律:

对于第i个二项式的第k项的指数,它是这样构成的:即由第i个数的权值(即硬币面值)*k(k为当前面值的硬币的个数,k的范围为0-k);

 

上面介绍的是每种硬币的数量为定值,如果每种硬币的数量为无穷多个,那该如何表示?

面值为2的硬币可以表示的函数为 x^0+x^2+x^4+x^6+...;

面值为3的硬币可以表示的函数为 x^0+x^3+x^6+x^9+...;

面值为5的硬币可以表示的函数为 x^0+x^5+x^10+x^15+...;

 

通过上面的构造函数,也可以发现大概也只有这三种问题:

1,给你多种东西,每种只有一个,求构造一个X的组合数

2,给你多种东西,每种有mi个,求构造一个X的组合数

3,给你多种东西,每种无穷多,求构造一个X的组合数

通过上面的三个问题,我们又会发现,只要构造出来的二项式的指数小于等于X,那么它就是一个合理的构造,现在的问题就转化为用函数构造二项式;

说完上面的普通母函数,也说一下指数型母函数:

 

其实这个不算是一个问题,因为有模板得意

模板之一如下:

memset(c1,0,sizeof(c1));
c1[0]=1;                                  //初始化
for(int i=1; i<=m; i++)                   //循环每个因子,一般来说种类数(因子数)都会给出来
{
    memset(c2,0,sizeof(c2));
    for(int j=0; j*v[i]<=n; j++)//每个因子的每一项---->1
    {
        for(int k=0; k+j*v[i]<=n; k++)//循环c1数组的每一项----->2
            c2[k+j*v[i]] += c1[k];
    }
    memcpy(c1,c2,sizeof(c2));
}
printf("%d\n",c1[n]);

这里详细说一下1,2位置的不同处理方法:

对于位置1,循环的是每个因子的每一项,一般情况下每个因子的权值是已知的,需要组合出的数也是已知的,如果每个因子的个数num【i】都知道,则限制条件就是

j<=num[i]&&j*v[i]<=n;如果每个因子的个数无穷多,则j<=num[i]可省略;

对于位置2,循环的事c1数组里的每一项,故k从0开始循环,并且要保证k+j*v【i】小于等于要组合出的数n

对于模板之二,其实就是和模板一差不多,不过模板二对每次循环的边界加了控制,避免的不必要的循环;

模板之二如下:

//假设v[i]为第i个物品的价值,num[i]为第i个物品的数量
memset(c1,0,sizeof(c1));
c1[0]=1;
int max=0,max1;//max为当前c1数组里最大的指数,即最大能组合出的数
for(int i=1; i<=n; i++)//循环每个因子
{
    max1=max+v[i]*num[i];//每次循环的界限控制的恰到好处
    for(int j=0; j<=num[i]; j++)//每个因子的每一项,共有num[i]+1项
    {
        for(int k=0; k<=max; k++)//循环c1数组的每一项
            c2[k+j*v[i]] += c1[k];
    }
    max=max1;
    for(int i=0; i<=max1; i++)
        c1[i]=c2[i],c2[i]=0;
}

对于模板三,就是平常不会经常见到的指数型母函数模板,用处不很大,因为要涉及到阶乘运算,所以数据范围大的话就会溢出;

模板之三如下:

#include <stdio.h>
#include <string.h>
const int MAX=30;
double c1[MAX],c2[MAX];
int f[11];
void fib(int n)//计算阶乘
{
    f[0]=1;
    for(int i=1; i<=n; i++)
        f[i]=f[i-1]*i;
}
int main()
{
    fib(10);
    int N,M,num[110];
    while(~scanf("%d%d",&N,&M))
    {
        for(int i=1; i<=N; i++)
            scanf("%d",&num[i]);
        memset(c1,0,sizeof(c1));
        c1[0]=1;
        for(int i=1; i<=N; i++)//循环每个因子
        {
            memset(c2,0,sizeof(c2));
            for(int j=0; j<=num[i]&&j<=M; j++) //每个因子的每一项
                for(int k=0; k+j<=M; k++) //循环c1数组的每一项
                    c2[k+j] += (c1[k]/f[j]);
            memcpy(c1,c2,sizeof(c2));
        }
        printf("%.lf\n",c1[M]*f[M]);
    }
    return 0;
}

其实这是hdu 1521题,感觉细节很多,就直接全部写上去了,首先 c1,c2为double型;其次,int型最多能存下12!,13!就超int了,long long(__int64)最多能存下20!;

然后就是循环,和普通母函数一样,就计算时略微不同,这个对照上面的指数母函数多项式理解;最后就是输出用double型.lf,应该就这么多了。。。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值