先从几个简单的问题开始(普通型):

问题1:有面值为1元、2元、3元的纸币各一张,要凑出3元,有几种取法?

很容易就能得出是2种取法。
如果用x的指数表示面值,可以得到母函数G(x)=(1+x)(1+x2)(1+x3)
将其展开得:G(x)=1+x+x2+2x3+x4+x5+x6
x3前面的系数代表了凑出面值为3的方案数。
解释: 因为多项式展开时的运算类似于排列组合,并且两个项相乘其实就是x指数的相加,所以最终可以用系数来表示方案数。这就是普通型母函数。
关于括号里为什么是1+xn的形式:1表示某种面值的纸币不取。(可以看成x0,取一张面值为0的纸币)。

变式1:如果纸币的数量无限,有几种取法?

这时的母函数可以表示为:G(x)=(1+x+x2+…+xn)(1+x2+x4+…+x2n)(1+x3+x6+…+x3n)
解释: 第一个括号表示面值为1元的纸币的取法,第二个括号表示2元的,第三个表示3元的。因为展开后的一个项是从三个括号里面各取一项进行相乘,所以用括号里x的系数来表示某个面值的纸币取的数量×面值。
题目要求面值和是3元,因此只要展开到x3的项即可,后面的可以不用去管。
展开后的G(x)=1+x+2x2+3x3+…
此时x3前面的系数是3,表示有三种取法。

变式2:如果面值为1元、2元、3元的纸币分别为3,2,1张,有几种取法?

跟上面做法类似,令G(x)=(1+x+x2+x3)(1+x2+x4)(1+x3)
因为是有限张,所以括号内的项数也是有限的,其余做法略。

问题2:有一个字符串:aaabbccc,从字符串内取出4个字符,组成一个新的字符串,有几种取法(要求按字母表顺序排列)?

和问题1类似,但是结果只对取出字母的数量有要求,而问题一对纸币数量无要求,只对面值和有要求。
因此此题的母函数可以写成:G(x)=(1+x+x2+x3)(1+x+x2)(1+x+x2+x3)
展开后得:G(x)=1+3x+6x2+9x3+10x4+9x5+6x6+3x7+x8
解释: 括号内的x指数表示字母出现的次数,从左到右的括号分别为abc。展开后项的指数就表示新的字符串长度。x4的系数为10,因此有10种取法。

普通母函数大致介绍完了,那么如何用代码来实现函数的展开呢?

手工计算的做法是先将前两个括号相乘,得到一个新的括号,再将新的括号与后面的括号依次相乘。代码的实现也是同样的做法,只不过省略了一些步骤,比如最终结果是要求x5前面的系数,那么计算的时候算到x5过就行,后面的计算全部跳过,因为对结果不会产生影响了。
以下为问题2的模板:

#include <bits/stdc++.h>
#define MAXN 10
using namespace std;int c1[MAXN], c2[MAXN]; //c1保存结果,c2存储运算时的中间量
int element[MAXN]; //表示每种字母有几个//n表示有n个括号(或n种字母),r表示要取出r个字母(或结果计算到指数为r的项就停止)
void get_ans(int n, int r)
{memset(c1, 0, sizeof(c1));memset(c2, 0, sizeof(c2));//将c1初始化为第一个括号的系数for (int i = 0; i <= element[0]; i++)c1[i] = 1;//第一层循环表示第i+1个括号for (int i = 1; i < n; i++){//第二层循环表示i+1个括号内指数为j的项for (int j = min(element[i], r); j >= 0; j--){//第三重循环表示前面计算结果中指数为k的项for (int k = r - j; k >= 0; k--){//合并同类项,注意是加上前面结果的系数c2[k + j] += c1[k];}}//一个括号计算完后将c2的结果转移至c1for (int j = r; j >= 0; j--){c1[j] = c2[j];c2[j] = 0;}}
}int main()
{element[0] = 3;element[1] = 2;element[2] = 3;get_ans(3, 8);for (int i = 0; i <= 8; i++) //输出所有项的系数printf("%d ", c1[i]);printf("\n");//输出为:1 3 6 9 10 9 6 3 1 符合结果系数return 0;
}

以下为变式1的模板:

#include <bits/stdc++.h>
#define MAXN 10
using namespace std;int c1[MAXN], c2[MAXN]; //c1保存结果,c2存储运算时的中间量
int element[MAXN];      //表示纸币的面值void get_ans(int n, int r)
{memset(c1, 0, sizeof(c1));memset(c2, 0, sizeof(c2));for (int i = 0; i <= r; i += element[0])c1[i] = 1;//第一层循环表示第i+1个括号for (int i = 1; i < n; i++){//第二层循环表示i+1个括号内指数为j的项(每次加上面值)for (int j = 0; j <= r; j += element[i]){//第三重循环表示前面计算结果中指数为k的项(因为不知道有多少项,要把所有指数都遍历一遍)for (int k = r - j; k >= 0; k--){//合并同类项,注意是加上前面结果的系数c2[k + j] += c1[k];}}//一个括号计算完后将c2的结果转移至c1for (int j = r; j >= 0; j--){c1[j] = c2[j];c2[j] = 0;}}
}int main()
{element[0] = 1;element[1] = 2;element[2] = 3;get_ans(3, 3);for (int i = 0; i <= 3; i++) //输出所有项的系数printf("%d ", c1[i]);printf("\n");//输出为:1 1 2 3 符合结果系数return 0;
}

如果排列的顺序不同视为不同的取法(指数型):

相比普通型母函数,指数型母函数除了相应指数的阶乘

因为取三个a,如果这三个a如果看成是不一样的,有3!种排列方式。(至于为什么这么做,跟最终结果的取法有关)
那么展开后很显然xn的系数是一个分数,这个分数乘上对于指数的阶乘就是结果数了。因为n个字符有n!中排列方式(算上重复的),而在未展开时除的对应指数的阶乘就是为了消除这种重复,也就解答了上面的问题。
代码跟普通型母函数差不多,只是c1和c2从int类型变成了double类型。
为了加速阶乘运算,可以进行打表,事先将阶乘结果保存在数组里,要算阶乘的时候直接取即可。

以问题2为例,如果对于排列顺序没有要求,那么有几种排列?

//指数型母函数模板
#include <bits/stdc++.h>
#define MAXN 10
using namespace std;
double f[10];              //保存阶乘结果
double c1[MAXN], c2[MAXN]; //c1保存结果,c2存储运算时的中间量
int element[MAXN];         //表示字母数量void factorial(int n) //阶乘预处理
{f[0] = 1;for (int i = 1; i <= n; i++){f[i] = f[i - 1] * i;}
}void get_ans(int n, int r)
{memset(c1, 0, sizeof(c1));memset(c2, 0, sizeof(c2));//将c1初始化为第一个括号的系数for (int i = 0; i <= element[0]; i++)c1[i] = 1.0 / f[i];//第一层循环表示第i+1个括号for (int i = 1; i < n; i++){//第二层循环表示i+1个括号内指数为j的项for (int j = min(element[i], r); j >= 0; j--){//第三重循环表示前面计算结果中指数为k的项for (int k = r - j; k >= 0; k--){//合并同类项,注意是加上前面结果的系数c2[k + j] += c1[k] / f[j];}}//一个括号计算完后将c2的结果转移至c1for (int j = r; j >= 0; j--){c1[j] = c2[j];c2[j] = 0;}}
}int main()
{factorial(10);element[0] = 3;element[1] = 2;element[2] = 3;int r = 8;get_ans(3, r);for (int i = 0; i <= r; i++)printf("%.0f ", c1[i] * f[i]);return 0;
}

母函数(普通型、指数型)相关推荐

  1. 快乐地打牢基础(13)——普通型母函数和指数型母函数的应用

    母函数就是一列用来展示一串数字的挂衣架. --赫伯特·唯尔夫 . 一.普通型母函数 1.定义 对于任意数列 a 0 , a 1 , a 2 . . . a n a_0,a_1,a_2...a_n a0 ...

  2. 【组合数学】指数型母函数 应用 ( 多重集排列问题 | 不同球放在不同盒子里 | 奇/偶数序列的指数生成函数推导 )

    文章目录 多重集全排列公式 指数型母函数 处理多重集排列问题 引入 指数型母函数 处理多重集排列问题 公式推导 指数型母函数 处理 有限数字串问题 指数型母函数 处理 n 位数字串问题 指数型母函数 ...

  3. 【母函数|生成函数】学习母函数

    参考文献(尊重原创): 二项式定理 递归和母函数 母函数(对于初学者的最容易理解的) 例题: 普通母函数: 找单词                   题解 摆花                    ...

  4. 母函数详解(转 侵删)

    母函数与排列组合 在谈论母函数问题之前,我们先看一个简单的问题描述:假如有两组数据(A,B)和(C,D),每组中选出一个构成一个组合,总共有几种选法?很显然总共有4种选法:AC,AD,BC,BD.而且 ...

  5. 模板记录——赛前准备

    模板大法好 0.STL操作 vector 动态数组 String 字符串类型 stack 栈(先进后出) queue 队列(先进先出,公平原则) set 集合(去重性,有自动排序的功能) map 映射 ...

  6. hdu1521 指数型母函数

    排列组合 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  7. HDU 2065 红色病毒 指数型母函数+泰勒公式

    医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶 ...

  8. HDU 1085 Holding Bin-Laden Captive! 活捉本拉登(普通型母函数)

    题意: 有面值分别为1.2.5的硬币,分别有num_1.num_2.num_5个,问不能组成的最小面值是多少?(0<=每种硬币个数<=1000,组成的面值>0) 思路: 母函数解决. ...

  9. hdu 2065 指数型母函数

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=2065 医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被 ...

最新文章

  1. 毕业生的商业软件开发之路 --- C#基本语法-C#代码模块逻辑框架
  2. 大型企业都用什么web服务器呢?nginx
  3. Union-Find 算法应用
  4. Jenkins系列之五——通过Publish over SSH插件实现远程部署
  5. Python如何生成windows可执行的exe文件
  6. c++ 指针_C/C++学习笔记——C提高:指针强化
  7. 程序员晒追女神聊天截图,坦言第一次没经验,网友直呼凭实力单身
  8. Mac M1 百度网盘客户端无法打开,网络连接不上
  9. 计算机组成原理问题集合
  10. 解决删除symantec,需要输入密码的问题
  11. Androidd 高德地图功能系列开发(搜索模块)
  12. html实训大作业《基于HTML+CSS+JavaScript红色文化传媒网站(20页)》
  13. Java的多态性以及Annotion注解
  14. 新浪微博产品的细微体验,你发现了么?
  15. 翁恺 python_翁恺 - 主页
  16. MyBatis的核心对象
  17. KiTTY/cnKiTTY与PuTTY/cnPuTTY差异和使用的简单说明~~
  18. 西电数据挖掘实验1——二分网络上的链路预测
  19. 博学谷python_博学谷 python
  20. 【basler】Chapter5:basler相机C#代码实现与详解(基础版)

热门文章

  1. 本科毕业四年:工作,辞职,结婚,买房
  2. ABT 节点 AWS 部署官方指南 | ArcBlock 博客
  3. 方德系统突然启动不了,出现提示initramfs>
  4. 管理计算机应用自考,自考《管理系统中计算机应用》串讲
  5. 论文翻译5-LiveNAS-神经增强型实时流媒体_通过在线学习改进实时视频摄取
  6. 二级建造师和一级建造师考试到底难不难和考什么
  7. 全网最全的知识库管理工具综合评测和推荐:FlowUs、Baklib、简道云
  8. 创龙TI AM437x ARM Cortex-A9 CPU开发板JTAG仿真器接口、LED指示灯
  9. ②mysql数据库(增删改查)
  10. SQL语句编写(增、删、改、查、序列)