母函数(生成函数)

生成函数普通生成函数指数生成函数

1.普通生成函数用于解决多重集的组合问题

2.指数型母函数用于解决多重ji5的排列问题

母函数可以解决递归数列的通项问题:斐波那契数列、卡特兰数列等

母函数通常解决类似如下的问题:

给5张1元,4张2元,3张5元,要得到15元,有多少种组合?

某些时候会规定至少使用3张1元、1张2元、0张5元。

某些时候会规定有无数张1元、2元、5元。

……

解题过程:

首先要写出表达式,通常是多项式的乘积,每项由多个 x^{y} 组成。

通用表达式为:

(x^(v[0]*n1[0])+x^(v[0]*(n1[0]+1))+x^(v[0]*(n1[0]+2))+...+x^(v[0]*n2[0]))

扫描二维码关注公众号,回复: 3325014 查看本文章

(x^(v[1]*n1[1])+x^(v[1]*(n1[1]+1))+x^(v[1]*(n1[1]+2))+...+x^(v[1]*n2[1]))

                                        ...............

(x^(v[K]*n1[K])+x^(v[K]*(n1[K]+1))+x^(v[K]*(n1[K]+2))+...+x^(v[K]*n2[K]))

K对应具体问题中物品的种类数。

V[i]表示该乘积表达式第 i 个因子的权重,对应具体问题的每个物品的价值或者权重

n1[i]表示该乘积表达式第 i 个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个

n2[i]表示该乘积表达式第i个因子的终止系数,对应具体问题中的每个物品的最多个数,即最多要取多少个

解题的关键是要确定V,N1,N2数组的值

模板

//a为计算结果,b为中间结果
int a[MAX],b[MAX];
memset(a,0,sizeof a);
a[0]=1;
for(int i=1;i<=k;i++)//循环每个因子
{
    memset(b,0,sizeof(b));
    for(int j=n1[i];j<=n2[i]&&j*v[i]<=p;j++)//循环每个因子的每一项
        for(int k=0;k+j*v[i]<=p;k++)//循环a的每一项
         b[k+j*v[i]]+=a[k];//把结果加到对应位
    memccpy(a,b,sizeof b);//b赋值给a        
}

p是可能的最大指数,拿钞票组合的题来说,如果求15元有多少种组合,那么p就是15;如果问最小的不能拼出的数值,那么p就是所有钱加起来的和。

用一个last变量记录目前最大的指数,这样只需要在0--last上计算

改进模板

//初始化a,因为有last,所以这里无需初始化其他位
a[0]=1;
int last=0;
for (int i=0;i<K;i++)
{
	int last2=min(last+n[i]*v[i],P);//计算下一次的last
	memset(b,0,sizeof(int)*(last2+1));//只清空b[0..last2]
	for (int j=n1[i];j<=n2[i]&&j*v[i]<=last2;j++)//这里是last2
		for (int k=0;k<=last&&k+j*v[i]<=last2;k++)//这里一个是last,一个是last2
			b[k+j*v[i]]+=a[k];
	memcpy(a,b,sizeof(int)*(last2+1));//b赋值给a,只赋值0..last2
	last=last2;//更新last
}

例题

一、hdu 1085和hdu 1171两题套用了第二个模板,省略了代码中二三层循环里关于last2的条件(其实也可以加上)。

详见:

hdu1085 :http://blog.csdn.net/xiaofei_it/article/details/17041467

hdu 1171:http://blog.csdn.net/xiaofei_it/article/details/17041709

二、hdu 1398套用了第一个模板,因为n2中每一项为无穷大,所以n2数组就省略了。

详见:

hdu 1398:http://blog.csdn.net/xiaofei_it/article/details/17041815

三、hdu 2079、hdu 2082和hdu 2110三题直接套用了第二个模板。

详见:

hdu2079

http://blog.csdn.net/xiaofei_it/article/details/17042045

hdu2082:

http://blog.csdn.net/xiaofei_it/article/details/17042253

hdu 2110:

http://blog.csdn.net/xiaofei_it/article/details/17042421

另外,至于什么时候用第一个模板,什么时候用第二个模板,就看题目规模。 通常情况下,第一个模板就够用了,上面的那些用第二个模板的题目用第一个模板同样能AC。 但如果数据规模比较大(通常不会有这种情况),就要使用第二个模板了。 以上题目n1均为0。

四、hdu 2152是一道n1不为0的题目,我在这里分别套用第一个和第二个模板解题。

详见: hdu 2152:http://blog.csdn.net/xiaofei_it/article/details/17042497

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=130;
int a[maxn],b[maxn];
int n1[maxn],n2[maxn];
int v[maxn];
void init()
{
    memset(a,0,sizeof a);
    memset(b,0,sizeof b);
    memset(n1,0,sizeof n1);
    memset(n2,0,sizeof n2);
}
int main()
{
  for(int i=0;i<maxn;i++)
    v[i]=1;
  int n,m;
  while(scanf("%d%d",&n,&m)!=EOF){
        init();
    for(int i=1;i<=n;i++)
        scanf("%d%d",&n1[i],&n2[i]);
    a[0]=1;
    for(int i=1;i<=n;i++)
    {
        memset(b,0,sizeof b);
        for(int j=n1[i];j<=n2[i]&&j*v[i]<=m;j++)
            for(int k=0;k+j*v[i]<=m;k++)
            b[k+v[i]*j]+=a[k];
        //memcpy(a,b,sizeof b);
        for(int k=0;k<maxn;k++)
            a[k]=b[k];
    }
    printf("%d\n",a[m]);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_40507857/article/details/82824047
今日推荐