母函数代码思路的详细解释
2012-11-05 17:11
225 查看
该文章建立在你已经看过母函数的相关数学知识的基础上,如果没有看过,建议看一下 hdu 论坛的母函数课件,传送门:http://acm.hdu.edu.cn/forum/read.php?tid=3853
用一个最简单的例子说明代码:
硬币面值有1元、5元、10元、25元、50元,一共5种,对于一个钱数 money,可以有多少中兑现方法?
很容易地构造母函数
G(x) = (x^0 + x^1 + x^2 + …) * (x^0 + x^5 + x^10 + …) * (x^0 + x^10 + x^20 + …)
* (x^0 + x^25 + x^50 + x^75 + …) * (x^0 + x^50 + x^100 + x^150 + …)
数学上的思路就拆开这个多项式因式,看看 x^money 的系数是多少。所以,编程的关键是如何实现拆分?
思路是 先把前面两个因式拆开,得到一个因式,代替第二个因式;然后用第二个因式(求出来的因式)和第三个因式相乘,代替第三个因式;递归计算到最后一个因式,得出结果。
由于便于编程的实现,我们在 G(x) 前面加多一个因式 (1*x^0 + 0*x^1 + 0*x^2 + … ),这个因式为1,所以并不改变母函数的值。把已经计算出来的因式的系数存放到一个数组 a 里面,把当前因式与下一个因式乘起来的因式的系数放到另外一个数组 b 里面。两个因式乘完了,就把 b 数组里面的数据放到 a 数组里面,把 b 数组清空,重复刚才的步骤,直到最后一个因式都乘进去了,才结束。此时,a 数据里面存放的就是拆分开来的多项式的系数。
代码如下:
显而易见,a 数组和 b 数据是滚动操作的,可以用一个二维数组,外加一个滚动变量简单操作:
看了两段代码。看过背包的童鞋一般都联想到背包去了。
对于要求的 money ,理解为背包的大小,每种硬币的面值理解为货物的大小,a[i] 就是选择到当前这个硬币,里面放 i 大小货物的方法数。如果题目中硬币无限个,就是完全背包,如果有些限制条件,就是修改版的完全背包。循环到最后,a[money] 就是选择完全部硬币,背包中放 money 大小货物的方法数,也就是把 money 元兑换成各种硬币的方法数。
当然,用母函数的思路理解代码,也是可以的。这样子的话,基本可以解决 hdu 那个课件里面的题目,除了 hdu 2069 Coin Change。
Coin Change 题目中,有一个要求,就是每一种兑换的方式,里面的硬币数量不超过 1000 个。用母函数怎么解决这个问题?参考背包的方法——加多一维!代码如下:
看了几个代码,基本上,用母函数的思路敲出来的代码和用背包敲出来代码是基本一样的。这意味着母函数的思路其实是背包思路的一种。如果童鞋对于背包熟的话,基本可以很快地消化母函数的代码,当然,不熟悉背包的话,就像我一样,用母函数的思路敲代码。效果是一样的。
最后,把 hdu 那个课件最后一页的练习题粘到这里来:1028、1709、1085、1171、1398、2069、2152。
本文转自:http://www.cnblogs.com/lijunle/archive/2010/09/04/1817764.html
用一个最简单的例子说明代码:
硬币面值有1元、5元、10元、25元、50元,一共5种,对于一个钱数 money,可以有多少中兑现方法?
很容易地构造母函数
G(x) = (x^0 + x^1 + x^2 + …) * (x^0 + x^5 + x^10 + …) * (x^0 + x^10 + x^20 + …)
* (x^0 + x^25 + x^50 + x^75 + …) * (x^0 + x^50 + x^100 + x^150 + …)
数学上的思路就拆开这个多项式因式,看看 x^money 的系数是多少。所以,编程的关键是如何实现拆分?
思路是 先把前面两个因式拆开,得到一个因式,代替第二个因式;然后用第二个因式(求出来的因式)和第三个因式相乘,代替第三个因式;递归计算到最后一个因式,得出结果。
由于便于编程的实现,我们在 G(x) 前面加多一个因式 (1*x^0 + 0*x^1 + 0*x^2 + … ),这个因式为1,所以并不改变母函数的值。把已经计算出来的因式的系数存放到一个数组 a 里面,把当前因式与下一个因式乘起来的因式的系数放到另外一个数组 b 里面。两个因式乘完了,就把 b 数组里面的数据放到 a 数组里面,把 b 数组清空,重复刚才的步骤,直到最后一个因式都乘进去了,才结束。此时,a 数据里面存放的就是拆分开来的多项式的系数。
代码如下:
#define M 500 int a[M]; int b[M]; void GenerationFunction() { int cent[5] = {1, 5, 10, 25, 50}; memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b)); a[0] = 1; // 手动添加的一个多项式因式 (1*x^0 + 0*x^1 + 0*x^2 + … ) for(int i = 0; i < 5; i ++) // i 指向第几个多项式因式(从0开始算) { for(int j = 0; j < M; j ++) { // j 指向已经算出来的因式的指数,a[j] 存放的是指数为 j 的项的系数 for(int k = 0; j+k < M; k += cent[i]) { // k 代表下一个因式的指数,因式的指数是隔 cent[i] 递增的。 // 另外,因式中,指数为 k 的项的系数为 1 b[j+k] += a[j]; // 对于 a[j] * x^j * x^k = a[j] * x^(j+k) // 就把 x^(j+k) 的系数 a[j] 加到 存放当前答案的数组 b 里面 } } for(int j = 0; j < M; j ++) { // 把数组 b 元素滚动到 数组 a 去,以便递归进行乘法运算;另外,清空数组 b a[j] = b[j]; b[j] = 0; } } }
显而易见,a 数组和 b 数据是滚动操作的,可以用一个二维数组,外加一个滚动变量简单操作:
#define M 500 int ans[M]; int f = 0; void GenerationFunction() { int cent[5] = {1, 5, 10, 25, 50}; memset(ans, 0, sizeof(ans)); ans[0][0] = 1; // 初始化第一个多项式因式 for(int i = 0; i < 5; i ++) { for(int j = 0; j < M; j ++) { for(int k = 0; j+k < M; k += cent[i]) { ans[1-f][j+k] += ans[f][j]; } } memset(ans[f], 0, sizeof(ans[f])); // 清空当前数组,留待下一个循环用 f = 1 - f; // 修改滚动变量 } }
看了两段代码。看过背包的童鞋一般都联想到背包去了。
对于要求的 money ,理解为背包的大小,每种硬币的面值理解为货物的大小,a[i] 就是选择到当前这个硬币,里面放 i 大小货物的方法数。如果题目中硬币无限个,就是完全背包,如果有些限制条件,就是修改版的完全背包。循环到最后,a[money] 就是选择完全部硬币,背包中放 money 大小货物的方法数,也就是把 money 元兑换成各种硬币的方法数。
当然,用母函数的思路理解代码,也是可以的。这样子的话,基本可以解决 hdu 那个课件里面的题目,除了 hdu 2069 Coin Change。
Coin Change 题目中,有一个要求,就是每一种兑换的方式,里面的硬币数量不超过 1000 个。用母函数怎么解决这个问题?参考背包的方法——加多一维!代码如下:
#define M 300 int ans[2][M][101]; int f = 0; void GenerationFunction() { int cent[5] = {1, 5, 10, 25, 50}; ans[0][0][0] = 1; // 初始化 for(int i = 0; i < 5; i ++) { for(int j = 0; j < M; j ++) { for(int k = 0; j+k < M; k += cent[i]) { for(int g = 0; g+k/cent[i] < 101; g ++) { // ans[][j][g] 表示放 j 大小的货物,并且用 g 个物品放,的方法数 ans[1-f][j+k][g+k/cent[i]] += ans[f][j][g]; } } } memset(ans[f], 0, sizeof(ans[f])); f = 1 - f; } }
看了几个代码,基本上,用母函数的思路敲出来的代码和用背包敲出来代码是基本一样的。这意味着母函数的思路其实是背包思路的一种。如果童鞋对于背包熟的话,基本可以很快地消化母函数的代码,当然,不熟悉背包的话,就像我一样,用母函数的思路敲代码。效果是一样的。
最后,把 hdu 那个课件最后一页的练习题粘到这里来:1028、1709、1085、1171、1398、2069、2152。
本文转自:http://www.cnblogs.com/lijunle/archive/2010/09/04/1817764.html
相关文章推荐
- 用母函数的思路解释母函数的代码
- Android App更新版本以及提示用户下载最新apk(有图,有代码,有最终效果图)详细解释及思路
- 用母函数的思路解释母函数的代码
- 有 Return 的情况下 Try Catch Finally的执行顺序(详细的代码以及解释)
- 第四章:LABEL_FILENAME_FOUND,LABEL_GOON_LOADING_FILE,GetFATEntry ,ReadSector四段代码一起详细解释
- 数据结构 - 线性表(顺序表)C语言代码实现-处理整型数据(附详细解释)。 _清风明月
- 带详细解释的冲击波原代码
- SSD和Textboxes 原理以区别,以及代码的详细解释(主要是写给我自己看的)
- NLTK找出最频繁的名词标记的程序(代码详细解释)
- 意外获得一段“飘窗”的代码,配有很详细的图文解释,下次要用“飘窗”,记得来找我!
- 0-1背包详细解释加代码注释
- 四种内部类详细解释和代码示例
- OpenCv学习笔记(三)---OpenCv中基本数据类型--Point,Size,Rect,Scalar,Vec3b类类型的详细解释及其OpenCv中源代码的详细分析
- IProximityOperator接口的详细解释---附代码
- 软件工程网络工程第二次训练(AC代码和详细解释)(C语言描述)
- 谁能帮我详细解释一下这段代码?
- Spring单例与线程安全小结--一个比较详细思路清晰很多对术语解释的贴
- Prototype 1.4.0源码详细解释--脚本代码全文注释
- 根据当前日期求上周一,上周日,上上周一,上周日的日期(详细解释代码)
- IIS详细错误代码以及解释