母函数及代码实现

数论|母函数|学习笔记|新手入门


母函数

母函数分为普通型母函数、指数型母函数、L级数等,普通型母函数使用较多。

利用的思想:
把组合问题的加法法则和幂级数的乘幂对应起来。

1.普通型母函数

普通型母函数(即普通型生成函数)用于解决多重集的组合问题。
通过构造一个多项式函数,使多项式各项的系数、次数等与多重集联系起来。

若函数 G(x)= ∑ an * xn = a0+a1* x+a2* x 2+···+an*xn
称G(x)是序列a0,a1,a2,…,an的普通型生成函数

例如有质量分别为1g,3g,5g的砝码各2个,问能称出质量为num的物品的组合方案有多少?
构造母函数G(x)=(1+x+x2)(1+x3+x6)(1+x5+x10)
某一括号内x的指数即为:对于某一种砝码种类,取不同数量的选择下的质量和
转换后G(x)=1+x+x2+x3+x4+2x5+2x6+2x7……
x的指数即为不同组合后的重量和,x的系数为组成重量大小为 x的指数 的方案数
展开过程中(多项式从左往右逐个相乘)可以看成对于同种重量的砝码选择不同数量加入到左边没有这种砝码的状态的过程。

模板如下:

#include <iostream>
#include <string.h>
using namespace std;
int main()
{
	int T, m, num, a[10], b[10], c1[51], c2[51];
//c1[i]  i的组成方案数
//c2[i]  临时存放每乘一项后的结果
	cin>>T;
	while (T--) {
		cin>>num>>m;//num 目标和
		for (int i = 1;i <= m;i++)
		cin>>a[i]>>b[i];//a[]保存重量 b[]保存数量
		memset(c1, 0, sizeof(c1));
		memset(c2, 0, sizeof(c2));
		c1[0] = 1;//第0项为1  最左边乘1对多项式无影响
		for (int i = 1;i <= m;i++) {//i表示乘到了第几个括号
		 for (int j = 0;j <= num;j++)//j表示左边项中每一项x^j的指数j(即不同数量的砝码组成的重量)        
		   {
			for (int k = 0;k + j <= num&&k <= b[i]*a[i];k += a[i])
			//k是右边项中每一项x^k的指数k 即重量 
			//重量和大于目标或砝码用完时循环停止
				//例如(1+x+x^2)(1+x^2+x^4+x^6+x^8) 
				//共四个2g砝码 对应指数:2*0=1 2*1=2 2*2=4 2*3=6 2*4=8
				c2[j+k]+=c1[j];//c2下标为x的次数 c2[j+k]的值为x^(j+k)的系数
			}
			memcpy(c1, c2, sizeof(c1));//把c2赋给c1
			memset(c2, 0, sizeof(c2));// c2清零
		}
		cout<<c1[num]<<endl;
	}
	return 0;
}

2.指数型函数

指数型母函数(即指数型生成函数)用于解决多重集的排列问题。

对序列a0,a1,a2,···an
若元素各不相同则全排列可得n!个不同全排列。
与高中排列组合 C 类似,若a1重复n1次,a2重复n2次···ak重复nk次,且∑nk=n
则有重复元素的序列a0,a1,a2···ak进行全排列所得的不同排列方案数为
n!/n1 ! *n2 ! *n3 !···nk!

构造G(x) = (1+x1/1!+x2/2!+……+xn1/(n1)!) (1+x1/1!+x2/2!+……+xn2/(n2)!)……(1+x1/1!+x2/2!+………+xnk/(nk!))

转换后排列方案数序列a0,a1,a2,···,ak的指数型母函数为
G(x)=a0+a1* x1/1!+a2* x2/2!···ak *xk/k!

模板如下:

#include<iostream>
#include<string.h>
using namespace std;
int  c1[52];
void calculate()
{
    c1[0]=c1[1]=1;
    for(int i=2;i<=10;i++)
         c1[i]=i*c1[i-1];
}
int main()
{
    int n,m,num[52];  
    double c[52],temp[52];
   calculate();
    while(~scanf("%d%d",&n,&m)){
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
           scanf("%d",&num[i]);
        for(int i=0;i<=num[1];i++)  
            c[i]=1.0/c1[i];
        for(int i=2;i<=n;i++)
		{    //i表示运算到第几个括号
              memset(temp,0,sizeof(temp));
          for(int j=0;j<=m;j++)    
              for(int k=0;k<=num[i]&&k+j<=m;k++)
                      temp[k+j]+=(1.0*c[j]/c1[k]);
             for(int j=0;j<=m;j++)
                  c[j]=temp[j];
        }
         printf("%.0f\n",c[m]*c1[m]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值