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

首先,我们引进一个小小概念来方便描述吧,record[n][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[n][m]=record[n][n];

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

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

那么其递归式:

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

record[n][n] (n<m)

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

record[n-m][m]+record[n][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个无标志的盒子,盒子允许空,也允许放多于一个球)。

整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。

 

 

母函数模版http://www.cnblogs.com/zhuy/archive/2012/08/07/2626047.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[n]<<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[n]<<endl;
}
 
return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值