深度解析母函数算法

深度解析母函数算法

1. 生成函数(母函数)

在数学中,某个序列 (an)n∈N 的母函数(又称生成函数,英语:Generating function)是一种形式幂级数,其每一项的系数可以提供关于这个序列的信息。使用母函数解决问题的方法称为母函数方法。

母函数可分为很多种,包括普通母函数、指数母函数、L级数、贝尔级数和狄利克雷级数。对每个序列都可以写出以上每个类型的一个母函数。构造母函数的目的一般是为了解决某个特定的问题,因此选用何种母函数视乎序列本身的特性和问题的类型。

母函数,又称生成函数,是ACM竞赛中经常使用的一种解题算法,常用来解决组合方面的题目。

生成函数的定义:g(x)=a0+a1x+a2x2+a3x3+… 称g(x) 是序列(a0,a1,a2,a3…)的生成函数;

一、普通母函数

下面咱们以题目解答的形式,来深入的探讨普通母函数的原理

1. 有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?

我们用母函数来解决这个问题

1个1克砝码可以看成1+x^1,1表示不取,x^1表示取一个,以下同理
1个2克砝码可以看成1+x^2
1个3克砝码可以看成1+x^3
1个4克砝码可以看成1+x^4

那么生成函数就是

g(x)=(1+x1)(1+x2)(1+x3)(1+x4)
=1+x+x2+2x3+2x4+2x5+2x6+2x7+x8+x9+x10

这个函数中可以看出重量为3克的方案有两种,重量为7的方案有两种,重量为10的有1种。

不难发现指数表示重量,系数表示方案数。

2. 求用1分、2分、3分的邮票贴出不同数值的方案数:大家把这种情况和第一种比较有何区别?第一种每种是一个,而这里每种是无限的。

那么生成函数就是

g(x)=(1+x+x2+x3+…)(1+x2+x4+x6+…)(1+x3+x6+x9+…)

以展开后的x^4为例,其系数为4,即4拆分成1、2、3之和的拆分方案数为4;

即 :4=1+1+1+1=1+1+2=1+3=2+2

//编程解决
/*
author: 熊谦智
题目描述:母函数模板 
涉及算法:这种母函数是表示从每一类中每次能拿一个价值是a[i]的元素  可以拿无限多个
*/
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 105, M = 1e6+5;
int dp[M], temp[M];
int main() {
  int n, target; // n 表示数的种类  target 表示最终要求的值的方案数 
  while (cin >> n >> target) {
    memset(dp,0,sizeof(dp));
    memset(temp,0,sizeof(temp));
    // 输入每个种类所表示的值 
    for (int i = 0; i < n; i++) {
      cin >> a[i];
    }
    sort(a,a+n); 
    // 先用第一种 (最小的一种)初始化 dp数组  -- 对应母函数的第一个括号 
    for (int i = 0; i < target; i+=a[0]) { // ---- ① 
      dp[i] = 1;
    }
    for (int i = 1; i < n; i++) { // ----- ②
      for (int j = 0; j <= target; j++) { // ----- ③
        for (int k = 0; k+j <= target; k+=a[i]) {  // ---- ④
          temp[k+j] += dp[j];
        }
      }
      for (int j = 0; j <= target; j++) {  // ---- ⑤
        dp[j] = temp[j];
        temp[j] = 0;
      }
    }
    cout << dp[target] << endl;
     
  }
}
①  、首先对dp初始化,由第一个表达式(1+x+x^2+..x^n)初始化,把质量从0到n的所有砝码都初始化为1.

②  、 i从2到n遍历,这里i就是指第i个表达式,上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。

③、j 从0到n遍历,这里j就是(前面i個表达式累乘的表达式)里第j个变量,如(1+x)(1+x^2)(1+x^3),j先指示的是1和x的系数,i=2执行完之后变为
(1+x+x^2+x^3)(1+x^3),这时候j应该指示的是合并后的第一个括号的四个变量的系数。

④ 、 k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。

⑤  、把temp的值赋给dp,而把temp初始化为0,因为temp每次是从一个表达式中开始的。

例题 :

  1. 可以搜蓝桥杯 包子凑数 这题既可以用完全背包 也可以用母函数来解答
  2. nyoj -90-数的划分 http://nyoj.top/problem/90
  3. hdu 1085(硬币问题)
  4. nyoj-588-Money
在网上你总能看到该题的不同代码实现,例如
// 之所以可以用下面的代码实现是因为题目中给出的就是 a[i] = i
// 而当a[i] != i  就不能用下面的代码实现
#include <iostream>
using namespace std;
const int _max = 10001; 
// c1是保存各项质量砝码可以组合的数目
// c2是中间量,保存每一次的情况
int c1[_max], c2[_max];   
int main()
{	//int n,i,j,k;
	int nNum;   // 
	int i, j, k;
 
	while(cin >> nNum)
	{
		for(i=0; i<=nNum; ++i)   // ---- ①
		{
			c1[i] = 1;
			c2[i] = 0;
		}
		for(i=2; i<=nNum; ++i)   // ----- ②
		{
 
			for(j=0; j<=nNum; ++j)   // ----- ③
				for(k=0; k+j<=nNum; k+=i)  // ---- ④
				{
					c2[j+k] += c1[j];
				}
				for(j=0; j<=nNum; ++j)     // ---- ⑤
				{
					c1[j] = c2[j];
					c2[j] = 0;
				}
		}
		cout << c1[nNum] << endl;
	}
	return 0;
}
3. 设有n个标志为1,2,…,n的网袋,第i个(i=1,2,…n)网袋里放有ni个球。不同网袋里的球是不同的,而同一网袋里的球则是没有差别的,认为是相同的。询问从中取r个球的方案数。

设生成函数

第几个大括号表示是第几个袋子 x 的指数表示从中取多少个球

g(x)=(1+x1+x2+…+xn1)(1+x1+x2+…+xn2)…

最后指数为r的那一项的系数就是方案数。

/*
author: 熊谦智
题目描述:
涉及算法:这种母函数是表示从每一类中可以抽取 1-a[i] 个
*/
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 105, M = 1e6+5;
int dp[M], temp[M];
int main() {
  int n, target;
  while (cin >> n >> target) {
    memset(dp,0,sizeof(dp));
    memset(temp,0,sizeof(temp));
    // 输入每个种类 的个数 
    for (int i = 0; i < n; i++) {
      cin >> a[i];
    }
    sort(a,a+n); 
    // 先用第一种 (最小的一种)初始化 dp数组  -- 对应母函数的第一个括号 
    for (int i = 0; i <= a[0] && i <= target; i++) { // ---- ① 
      dp[i] = 1;
    }
    for (int i = 1; i < n; i++) {
      for (int j = 0; j <= target; j++) {
        for (int k = 0; k+j <= target && k <= a[i]; k++) {
          temp[k+j] += dp[j];
        }
      }
      for (int j = 0; j <= target; j++) {
        dp[j] = temp[j];
        temp[j] = 0;
      }
    }
    cout << dp[target] << endl;
  }
} 
/*
// 表示有3个网袋 从中选取10个求  共有多少总不同的方法 
3 10 
4 2 9
*/

总结一下,生成函数大多用来解决有限或无限物体的组合方案。


参考链接:
https://blog.csdn.net/vsooda/article/details/7975485
https://blog.csdn.net/qq_41357771/article/details/83449481
https://blog.csdn.net/yu121380/article/details/79914529
https://www.cnblogs.com/13224ACMer/p/4671551.html

指数型指数函数

https://blog.csdn.net/shouwang_tomorrow/article/details/49157527

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值