母函数(普通型、指数型)
先从几个简单的问题开始(普通型):
问题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;
}
母函数(普通型、指数型)相关推荐
- 快乐地打牢基础(13)——普通型母函数和指数型母函数的应用
母函数就是一列用来展示一串数字的挂衣架. --赫伯特·唯尔夫 . 一.普通型母函数 1.定义 对于任意数列 a 0 , a 1 , a 2 . . . a n a_0,a_1,a_2...a_n a0 ...
- 【组合数学】指数型母函数 应用 ( 多重集排列问题 | 不同球放在不同盒子里 | 奇/偶数序列的指数生成函数推导 )
文章目录 多重集全排列公式 指数型母函数 处理多重集排列问题 引入 指数型母函数 处理多重集排列问题 公式推导 指数型母函数 处理 有限数字串问题 指数型母函数 处理 n 位数字串问题 指数型母函数 ...
- 【母函数|生成函数】学习母函数
参考文献(尊重原创): 二项式定理 递归和母函数 母函数(对于初学者的最容易理解的) 例题: 普通母函数: 找单词 题解 摆花 ...
- 母函数详解(转 侵删)
母函数与排列组合 在谈论母函数问题之前,我们先看一个简单的问题描述:假如有两组数据(A,B)和(C,D),每组中选出一个构成一个组合,总共有几种选法?很显然总共有4种选法:AC,AD,BC,BD.而且 ...
- 模板记录——赛前准备
模板大法好 0.STL操作 vector 动态数组 String 字符串类型 stack 栈(先进后出) queue 队列(先进先出,公平原则) set 集合(去重性,有自动排序的功能) map 映射 ...
- hdu1521 指数型母函数
排列组合 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- HDU 2065 红色病毒 指数型母函数+泰勒公式
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶 ...
- HDU 1085 Holding Bin-Laden Captive! 活捉本拉登(普通型母函数)
题意: 有面值分别为1.2.5的硬币,分别有num_1.num_2.num_5个,问不能组成的最小面值是多少?(0<=每种硬币个数<=1000,组成的面值>0) 思路: 母函数解决. ...
- hdu 2065 指数型母函数
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2065 医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被 ...
最新文章
- 毕业生的商业软件开发之路 --- C#基本语法-C#代码模块逻辑框架
- 大型企业都用什么web服务器呢?nginx
- Union-Find 算法应用
- Jenkins系列之五——通过Publish over SSH插件实现远程部署
- Python如何生成windows可执行的exe文件
- c++ 指针_C/C++学习笔记——C提高:指针强化
- 程序员晒追女神聊天截图,坦言第一次没经验,网友直呼凭实力单身
- Mac M1 百度网盘客户端无法打开,网络连接不上
- 计算机组成原理问题集合
- 解决删除symantec,需要输入密码的问题
- Androidd 高德地图功能系列开发(搜索模块)
- html实训大作业《基于HTML+CSS+JavaScript红色文化传媒网站(20页)》
- Java的多态性以及Annotion注解
- 新浪微博产品的细微体验,你发现了么?
- 翁恺 python_翁恺 - 主页
- MyBatis的核心对象
- KiTTY/cnKiTTY与PuTTY/cnPuTTY差异和使用的简单说明~~
- 西电数据挖掘实验1——二分网络上的链路预测
- 博学谷python_博学谷 python
- 【basler】Chapter5:basler相机C#代码实现与详解(基础版)
热门文章
- 本科毕业四年:工作,辞职,结婚,买房
- ABT 节点 AWS 部署官方指南 | ArcBlock 博客
- 方德系统突然启动不了,出现提示initramfs>
- 管理计算机应用自考,自考《管理系统中计算机应用》串讲
- 论文翻译5-LiveNAS-神经增强型实时流媒体_通过在线学习改进实时视频摄取
- 二级建造师和一级建造师考试到底难不难和考什么
- 全网最全的知识库管理工具综合评测和推荐:FlowUs、Baklib、简道云
- 创龙TI AM437x ARM Cortex-A9 CPU开发板JTAG仿真器接口、LED指示灯
- ②mysql数据库(增删改查)
- SQL语句编写(增、删、改、查、序列)