您的位置:首页 > 其它

数论——拆分数++母函数模版

2012-11-10 18:13 190 查看
首先,我们引进一个小小概念来方便描述吧,record
[m]是把自然数划分成所有元素不大于m的分法,例如:

当n=4,m=1时,要求所有的元素都比m小,所以划分法只有1种:{1,1,1,1};

当n=4,m=2时,。。。。。。。。。。。。。。。。只有3种{1,1,1,1},{2,1,1},{2,2};

当n=4,m=3时,。。。。。。。。。。。。。。。。只有4种{1,1,1,1},{2,1,1},{2,2},{3,1};

当n=4,m=5时,。。。。。。。。。。。。。。。。只有5种{1,1,1,1},{2,1,1},{2,2},{3,1},{4};

从上面我们可以发现:当n==1||m==1时,只有一种分法;

当n<m时,由于分法不可能出现负数,所以record
[m]=record

;

当n==m时,那么就得分析是否要分出m这一个数,如果要分那就只有一种{m},要是不分,那就是把n分成不大于m-1的若干份;即record

=1+record
[n-1];

当n>m时,那么就得分析是否要分出m这一个数,如果要分那就{{m},{x1,x2,x3..}}时n-m的分法record[n-m][m],要是不分,那就是把n分成不大于m-1的若干份;即record
[m]=record[n-m][m]+record
[m-1];

那么其递归式:

record
[m]= 1 (n==1||m==1)

record

(n<m)

1+record
[m-1] (n==m)

record[n-m][m]+record
[m-1] (N>m)

——————————————————————————————————————————

把加法变为幂运算

这里先给出2个例子,等会再结合题目分析:

第一种:
有1克、2克、3克、4克的砝码各一 枚,能称出哪几种重量?每种重量各有几种可能方案?
考虑用母函数来接吻这个问题:
我们假设x表示砝码,x的指数表示砝码的重量,这样:
1个1克的砝码可以用函数1+x表示,
1个2克的砝码可以用函数1+x2表示,
1个3克的砝码可以用函数1+x3表示,
1个4克的砝码可以用函数1+x4表示,
上面这四个式子懂吗?
我们拿1+x2来说,前面已经说过,x表示砝码,x的指数表示重量,即这里就是一个质量为2的砝码,那么前面的1表示什么?1代表重量为2的砝码数量为0个。(理解!)
不知道大家理解没,我们这里结合前面那句话:
“把组合问题的加法法则和幂级数的t的乘幂的相加对应起来”

1+x2表示了两种情况:1表示质量为2的砝码取0个的情况,x2表示质量为2的砝码取1个的情况。

这里说下各项系数的意义:
在x前面的系数a表示相应质量的砝码取a个,而1就表示相应砝码取0个,这里可不能简单的认为相应砝码取0个就该是0*x2(想下为何?结合数学式子)。

所以,前面说的那句话的意义大家可以理解了吧?

几种砝码的组合可以称重的情况,可以用以上几个函数的乘积表示:
(1+x)(1+x2)(1+x3)(1+x4)
=(1+x+x2+x3)(1+x3+x4+x7)
=1+x+x2+2x3+2x4+2x5+2x6+2x7+x8+x9+x10
从上面的函数知道:可称出从1克到10克,系数便是方案数。(!!!经典!!!)
例如右端有2x5 项,即称出5克的方案有2:5=3+2=4+1;同样,6=1+2+3=4+2;10=1+2+3+4。
故称出6克的方案有2,称出10克的方案有1 。

接着上面,接下来是第二种情况:

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



以展开后的x4为例,其系数为4,即4拆分成1、2、3之和的拆分数为4;
即 :4=1+1+1+1=1+1+2=1+3=2+2
这里再引出两个概念整数拆分和拆分数(没有顺序):
所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。
整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。

母函数模版/article/5479354.html

//made by syx
//time 2010年9月11日 10:17:27
//母函数例题

/*//整数拆分模板
#include <iostream>
using namespace std;
const int lmax=10000;
//c1是用来存放展开式的系数的,而c2则是用来计算时保存的,
//他是用下标来控制每一项的位置,比如 c2[3] 就是 x^3 的系数。
//用c1保存,然后在计算时用c2来保存变化的值。
int c1[lmax+1],c2[lmax+1];
int main()
{
int n, i, j, k ;
// 计算的方法还是模拟手动运算,一个括号一个括号的计算,
// 从前往后
while ( cin>>n ){
//对于 1+x+x^2+x^3+ 他们所有的系数都是 1
// 而 c2全部被初始化为0是因为以后要用到 c2[i] += x ;
for ( i=0; i<=n; i++ ){
c1[i]=1;
c2[i]=0;
}
//第一层循环是一共有 n 个小括号,而刚才已经算过一个了
//所以是从2 到 n
for (i=2; i<=n; i++){
// 第二层循环是把每一个小括号里面的每一项,都要与前一个
//小括号里面的每一项计算。
for ( j=0; j<=n; j++ )
//第三层小括号是要控制每一项里面 X 增加的比例
// 这就是为什么要用 k+= i ;
for ( k=0; k+j<=n; k+=i ){
// 合并同类项,他们的系数要加在一起,所以是加法,呵呵。
// 刚开始看的时候就卡在这里了。
c2[ j+k] += c1[ j];
}
// 刷新一下数据,继续下一次计算,就是下一个括号里面的每一项。
for ( j=0; j<=n; j++ ){
c1[j] = c2[j] ;
c2[j] = 0 ;
}
}
cout<<c1
<<endl;
}
return 0;
}
这句 c2[j+k] += c1[j];的理解还要自己好好的体会体会啊!
*/

自己理解:对于(#式)   (1+x+x^2+x^3+x^4+x^5+....)*(1+x^2+x^4+x^6+x^8+x^10+....)*(1+x^3+x^6+x^9+x^12....).....

第一个for给c1 和 c2 赋值,把上面#式的第一个括号(1+x+x^2+x^3+x^4+x^5+....)的系数给放在c1中,

从而再次计算从 # 的第二个括号开始, 所以 i 就是代表的# 式第几个括号,

而 用程序模拟手工计算,就是先计算第一个括号与第二个括号计算,把结果放到c2中,

在把结果与第三个括号计算,把结果放到c2中,在和第四个括号计算,........

所以 j 就是指的已经计算出的各项的系数 ,比如第一次之后 1+x+x^2+x^3+x^4+x^5+... , j=0指向1 ,

j=2 指向x , .... ,而 k 就是指将要计算的那个括号中的项,因为第i个括号,中的指数为0 , i , 2i....所以 k要 + i ;

而结果 c2[j+k] += c1[j]; 就是把以计算出的 j项的系数和现在正在计算的括号的k项相乘,所以指数为j+k ,所以结果放到c2[j+k] 中 ,这就是这几个for的作用!

最后刷新下结果,下一组数据计算!

//整数拆分母函数模板
#include <iostream>
using namespace std;
const int lmax=10000;
int c1[lmax+1],c2[lmax+1];
int main()
{ int n,i,j,k;
while (cin>>n)
{
//首先对c1初始化,由第一个表达式(1+x+x2+..xn)初始化,
//把质量从0到n的所有砝码都初始化为1.
for (i=0;i<=n;i++)
{
c1[i]=1;
c2[i]=0;
}

//i从2到n遍历,这里i就是指第i个表达式,
//上面给出的第二种母函数关系式里,每一个括号括起来的就是一个表达式。
for (i=2;i<=n;i++)
{
//j 从0到n遍历,这里j就是只一个表达式
//里第j个变量,比如在第二个表达式里:(1+x2+x4….)里,第j个就是x2*j.
for ( j=0;j<=n;j++)
for (k=0;k+j<=n;k+=i)//k表示的是第j个指数,所以k每次增i(因为第i个表达式的增量是i)。
c2[ j+k]+=c1[j];
//把c2的值赋给c1,而把c2初始化为0,因为c2每次是从一个表达式中开始的
for (j=0;j<=n;j++)
{
c1[j]=c2[j];
c2[j]=0;
}
}
cout<<c1
<<endl;
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: