编程语言
首页 > 编程语言> > 母函数(详细分析+例题讲解) 每日一遍,算法再见!

母函数(详细分析+例题讲解) 每日一遍,算法再见!

作者:互联网

母函数

母函数

一.普通母函数

在研究普通母函数之前,先看一个多项式,以便于更好的理解。
( 1 + a 1 x ) ( 1 + a 2 x ) ( 1 + a 3 x ) ( 1 + a 4 x ) . . . . . . ( 1 + a n x ) = 1 + ( a 1 + a 2 + . . . + a n ) x + ( a 1 a 2 + a 1 a 3 + . . . + a n − 1 a n ) x 2 + . . . + a 1 a 2 a 3 . . . a n x n (1+a_1x)(1+a_2x)(1+a_3x)(1+a_4x)......(1+a_nx) = 1+(a_1+a_2+...+a_n)x+(a_1a_2+a_1a_3+...+a_{n-1}a_n)x^2+...+a_1a_2a_3...a_nx^n (1+a1​x)(1+a2​x)(1+a3​x)(1+a4​x)......(1+an​x)=1+(a1​+a2​+...+an​)x+(a1​a2​+a1​a3​+...+an−1​an​)x2+...+a1​a2​a3​...an​xn.
我们由多项式可以看出
(1) x x x项的系数是从n个数 ( a 1 , a 2 , a 3 , . . . , a n ) (a_1,a_2,a_3,...,a_n) (a1​,a2​,a3​,...,an​)中取一个数的组合起来的全体,有C(n,1)=n个。

(2) x 2 x^2 x2项的系数是从n个数 ( a 1 , a 2 , a 3 , . . . , a n ) (a_1,a_2,a_3,...,a_n) (a1​,a2​,a3​,...,an​)中取两个数的组合的全体,有C(n,2)个.

(3) x n x^n xn项的系数是从n个数 ( a 1 , a 2 , a 3 , . . . , a n ) (a_1,a_2,a_3,...,a_n) (a1​,a2​,a3​,...,an​)中取n个数的组合的全体,有C(n,n)=1个.

于是按照上面的规律,我们可以得到这个多项式的表达式
( 1 + x ) n = 1 + C ( n , 1 ) x + C ( n , 2 ) x 2 + . . . + C ( n , n ) x n (1+x)^n =1+C(n,1)x+C(n,2)x^2+...+C(n,n)x^n (1+x)n=1+C(n,1)x+C(n,2)x2+...+C(n,n)xn.
我们把上述式字改写成 G ( x ) = ( 1 + x ) n = 1 + a 1 x + a 2 x 2 + a 3 x 3 . . . + a n x n G(x)=(1+x)^n = 1+a_1x+a_2x^2+a_3x^3...+a_nx^n G(x)=(1+x)n=1+a1​x+a2​x2+a3​x3...+an​xn,其中ai=C(n,i),1<=i<=n.
我们讲函数G(x)是序列 a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1​,a2​,a3​,...,an​的母函数。

例如G(x) = ( 1 + x ) n (1+x)^n (1+x)n是序列 C ( n , 1 ) , C ( n , 2 ) , . . . , C ( n , n ) C(n,1),C(n,2),...,C(n,n) C(n,1),C(n,2),...,C(n,n)的母函数

例1.有质量1,2,3的砝码各一枚,问:
(1)可以称出多少中不同质量的物品?
(2)要称出质量为3的物品,有多少种方案?

:一个1g的砝码我们可以用 1 + x 1+x 1+x表示,其中1表示不用1g的砝码, x x x表示用1g的砝码。

同理,一个2g砝码我们可以用 1 + x 2 1+x^2 1+x2表示,其中1表示不用2g的砝码, x 2 x^2 x2表示用2g的砝码。

同理,一个3g砝码我们可以用 1 + x 3 1+x^3 1+x3表示,其中1表示不用3g的砝码, x 3 x^3 x3表示用3g的砝码。

那么母函数 G ( x ) = ( 1 + x ) ( 1 + x 2 ) ( 1 + x 3 ) = 1 + x + x 2 + 2 x 3 + x 4 + x 5 + x 6 G(x) = (1+x)(1+x^2)(1+x^3)=1+x+x^2+2x^3+x^4+x^5+x^6 G(x)=(1+x)(1+x2)(1+x3)=1+x+x2+2x3+x4+x5+x6 ,我们发现可以称出1~6g的物品,其中x前面的系数就是称出这种重量的方案数,比如 2 x 3 2x^3 2x3,x的指数是3,系数为2,表示能称出质量为3g的方案数有2种。

那么我们假设1g,2g,3g的砝码都有无数个,那么这样的母函数会是什么样子呢?

所构造的母函数如下: G ( x ) = ( 1 + x + x 2 + x 3 + . . + x n ) ( 1 + x 2 + x 4 + . . . + x n ) ( 1 + x 3 + x 6 + . . . + x n ) . . . G(x) = (1+x+x^2+x^3+..+x^n)(1+x^2+x^4+...+x^n)(1+x^3+x^6+...+x^n)... G(x)=(1+x+x2+x3+..+xn)(1+x2+x4+...+xn)(1+x3+x6+...+xn)...,其中第一个括号中 ( 1 + x + x 2 + x 3 + . . + x n ) , 1 表 示 取 0 个 1 g 的 砝 码 , x 表 示 取 1 个 1 g 的 砝 码 , x 2 表 示 取 2 个 1 g 的 砝 码 . . . 依 次 类 推 (1+x+x^2+x^3+..+x^n),1表示取0个1g的砝码,x表示取1个1g的砝码,x^2表示取2个1g的砝码...依次类推 (1+x+x2+x3+..+xn),1表示取0个1g的砝码,x表示取1个1g的砝码,x2表示取2个1g的砝码...依次类推,那么第二个括号中 ( 1 + x 2 + x 4 + . . . + x n ) , 1 表 示 取 0 个 2 g 的 砝 码 , x 2 表 示 取 1 个 2 g 的 砝 码 , x 4 表 示 取 2 个 2 g 的 砝 码 . . . . 依 次 类 推 (1+x^2+x^4+...+x^n),1表示取0个2g的砝码,x^2表示取1个2g的砝码,x^4表示取2个2g的砝码....依次类推 (1+x2+x4+...+xn),1表示取0个2g的砝码,x2表示取1个2g的砝码,x4表示取2个2g的砝码....依次类推。

下面我们来做一道例题

练习题1

2006/1/15 ACM程序设计期末考试
解题思路:我们按照题意构造出一个母函数G(x),然后我们只需要求出G(x)的所有x的指数小于等于50的系数之和就是答案.

母函数构造如下: G ( x ) = ( 1 + x + x 2 + . . . + x x 1 ) ( 1 + x 2 + x 4 + . . . + x 2 x 2 ) ( 1 + x 3 + x 6 + . . . + x 3 x 3 . . . . ( 1 + x 26 + x 52 + . . . + x 26 x 26 ) ) G(x) = (1+x+x^2+...+x^{x_1})(1+x^2+x^4+...+x^{2x_2})(1+x^3+x^6+...+x^{3x_3}....(1+x^{26}+x^{52}+...+x^{26x_{26}})) G(x)=(1+x+x2+...+xx1​)(1+x2+x4+...+x2x2​)(1+x3+x6+...+x3x3​....(1+x26+x52+...+x26x26​))

AC代码:

#include<bits/stdc++.h>
using namespace std;
int a[60]={0},b[50]={0};
int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		a[0]=1;
		for(int i=1;i<=26;i++)
		{
			
			int num;
			cin>>num;
			if(num==0) continue;
			for(int j=0;j<=50;j++)
			{
			for(int k=0;k<=num&&k*i+j<=50;k++)
			{
				b[k*i+j]+=a[j];
			}
			}
			for(int i=0;i<=50;i++)
			{
				a[i]=b[i];
				b[i]=0;
			}
		}
		int sum=0;
		for(int i=1;i<=50;i++) sum+=a[i];
		cout<<sum<<'\n';
	}
	return 0;
}

练习题2(整数的拆分)

Ignatius and the Princess III
题目大意:给你一个正整数N,我们定义一个等式
N=a[1]+a[2]+a[3]+…+a[m],a[i]>0,且1<=m<=N.问题是给你一个正整数N,有多少种不同的等式,例如4=1+3
4=1+1+1+1
4=2+2
4=4
4=2+1+1,一共有5种

思路:构造母函数G(x),然后 x N x^N xN前面的系数就是方案数
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[200]={0},b[200]={0};
int main()
{
	int n;
	while(cin>>n)
	{
//	cin>>n;
    memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	a[0]=1;
	for(int i=1;i<=n;i++)
	{
		int num=n/i;
		for(int j=0;j<=n;j++)
		for(int k=0;k<=num&&k*i+j<=n;k++)
		{
			b[k*i+j]+=a[j];
		}
		
		for(int j=0;j<=n;j++) 
		{
			a[j]=b[j];
			b[j]=0;
		}
	}
	cout<<a[n]<<'\n';
    }
	return 0;
}

二.Ferrers图像

定义:一个从上到下的n层组成的图像,其中第i层有 m i m_i mi​格子,并且保证 m i > = m i + 1 m_i>=m_{i+1} mi​>=mi+1​。就像下图这样
在这里插入图片描述
然后我们发现把这张图顺时针旋转90度也是一个Ferrers图像,就像下面这样,我们称这两张图为一对共轭的Ferrers图像。

在这里插入图片描述
我们可以通过研究Ferrers图像得到一些性质
定理:整数N拆分成k个数的和的拆分方案数(简称拆分数),与数N拆分成最大数为k的拆分数相同。

例如,我们令N=34,分解k=5,那么
图一.
在这里插入图片描述
这图一就表示34 = 6+6+5+4+3,一共5个数

图二(图一的共轭Ferrers图).
在这里插入图片描述
图二就表示了,N=5+5+5+4+3+2,最大数为5.

故定理2:整数N拆分成不超过m的拆分数,与拆分成最大数不超过m的拆分数相同。

三.指数母函数

指数型母函数问题:
  假设有n个元素,其中a1,a2,····,an互不相同,进行全排列,可得n!个不同的排列。若其中某一元素a1重复了n1次,全排列出来必有重复元素,其中真正不同的排列数应为n!/n1!,即其重复度为n1!
同样理由a1重复了n1次,a2重复了n2次,····,ak重复了nk次,n1+n2+····+nk=n。对于这样的n个元素进行全排列,可得不同排列的个数实际上是

在这里插入图片描述
上面的问题是从n个元素中选n个元素(也就是全部选)的排列,那么如果要求从n个元素中选r个的排列有多少种怎么求呢?这里就要用到指数母函数了

定义:在这里插入图片描述
我们发现指数母函数和普通母函数只有一个地方不同,那就是x前面的系数,普通母函数系数是 a i a_i ai​的表示形式,而指数母函数系数是 a i i ! \frac{a_i}{i!} i!ai​​的表示形式。下面我们来看看指数形母函数能够解决什么样的问题。
例3.有1,2,3,4四个数字的组成的五位数,要求1出现次数不超过2次,但不能不出现;2出现次数不超过1次;3出现的次数最多3次,可以不出现;4出现次数为偶数,求满足上述条件的数的个数。

问题分析:想当于是从1,2,3,4四个数中可重复的选5个数组成的排列数,但是每个数字有选取限制条件。

所以我们构造出指数母函数在这里插入图片描述
能够看到 x 5 5 ! \frac{x^5}{5!} 5!x5​前面的系数是215,所以一共有215种情况。

练习题3

排列组合HDU-1521
这就是一道典型的指数母函数的板子题。
分析:我们构造出指数母函数在这里插入图片描述
AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double a[200],b[200];
int f(int n)
{
	int ans=1;
	for(int i=1;i<=n;i++) ans*=i;
	return ans;
}
int num[20];
int main()
{
	
	int n,m;
	while(cin>>n>>m)
	{
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));	
    for(int i=1;i<=n;i++) cin>>num[i];
    for(int i=0;i<=num[1];i++)
    {
    	a[i]=1.0/f(i);
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=0;j<=m;j++)
		 for(int k=0;k<=num[i]&&k+j<=m;k++)
		 {
		 	b[k+j]+=a[j]/f(k);
		 }
		 for(int i=0;i<=m;i++)
		 {
		 	a[i]=b[i];
		 	b[i]=0;
		 }
	}
	
	printf("%.0lf\n",a[m]*f(m));
    }
	return 0;
}

标签:...,+...+,函数,int,砝码,讲解,x2,详细分析,例题
来源: https://blog.csdn.net/TheWayForDream/article/details/114186470