(一)递归法
根据n和m的关系,考虑以下几种情况:
(1)当n=1时,不论m的值为多少(m>0),只有一种划分即{1};
(2) 当m=1时,不论n的值为多少,只有一种划分即n个1,{1,1,1,...,1};
(3) 当n=m时,根据划分中是否包含n,可以分为两种情况:
(a). 划分中包含n的情况,只有一个即{n};
(b). 划分中不包含n的情况,这时划分中最大的数字也一定比n小,即n的所有(n-1)划分。
因此 f(n,n) =1 + f(n,n-1);
(4) 当n<m时,由于划分中不可能出现负数,因此就相当于f(n,n);
(5) 但n>m时,根据划分中是否包含最大值m,可以分为两种情况:
(a). 划分中包含m的情况,即{m, {x1,x2,...xi}}, 其中{x1,x2,... xi} 的和为n-m,可能再次出现m,因此是(n-m)的m划分,因此这种划分
个数为f(n-m, m);
(b). 划分中不包含m的情况,则划分中所有值都比m小,即n的(m-1)划分,个数为f(n,m-1);
因此 f(n, m) = f(n-m, m)+f(n,m-1);
综合以上情况,我们可以看出,上面的结论具有递归定义特征,其中(1)和(2)属于回归条件,(3)和(4)属于特殊情况,将会转换为情况(5)。而情况(5)为通用情况,属于递推的方法,其本质主要是通过减小m以达到回归条件,从而解决问题。其递推表达式如下:
f(n, m)= 1; (n=1 or m=1)
f(n, n); (n<m)
1+ f(n, m-1); (n=m)
f(n-m,m)+f(n,m-1); (n>m)
代码:
1 #include<stdio.h> 2 int div(int n,int m) 3 { 4 if (n < m) 5 return div(n,n); 6 else if (n==1||m==1) 7 return 1; 8 else if (n==m) 9 return 1 + div(n,m-1); 10 else 11 { 12 return div(n,m-1) + div(n-m,m); 13 } 14 } 15 int main() 16 { 17 int n,m,t; 18 scanf("%d",&t); 19 while(t--) 20 { 21 scanf("%d%d",&n,&m); 22 printf("%d\n",div(n,m)); 23 } 24 return 0; 25 26 }
(二)母函数法
则对于从1到N的所有可能组合结果我们可以表示为:
G(x) = (1+x+x^2+x^3+...+x^n) (1+x^2+x^4+...) (1+x^3+x^6+...) ... (1+x^n)
= g(x,1) g(x,2) g(x,3) ... g(x, n)
= a0 + a1* x + a2* x^2 + ... + an* x^n + ... ; (展开式)
上面的表达式中,每一个括号内的多项式代表了数字i的参与到划分中的所有可能情况。因此该多项式展开后,由于x^a * x^b=x^(a+b),因此 x^i 就代表了i的划分,展开后(x^i)项的系数也就是i的所有划分的个数,即f(n,n)=an (上式中g(x,i)表示数字i的所有可能出现情况)。
由此我们找到了关于整数划分的母函数G(x);剩下的问题是,我们需要求出G(x)的展开后的所有系数。
为此我们首先要做多项式乘法,对于我们来说并不困难。我们把一个关于x的一元多项式用一个整数数组a[]表示,a[i]代表x^i的系数,即:
g(x) = a[0] + a[1]x + a[2]x^2 + ... + a[n]x^n;
1 多项式相乘,即c=a*b 2 3 #define N 130 4 5 unsigned long a[N];/*多项式a的系数数组*/ 6 7 unsigned long b[N];/*多项式b的系数数组*/ 8 9 unsigned long c[N];/*存储多项式a*b的结果*/ 10 11 12 13 /*两个多项式进行乘法,系数分别在a和b中,结果保存到c ,项最大次数到N */ 14 15 /*注意这里我们只需要计算到前N项就够了。*/ 16 17 void Poly() 18 19 { 20 21 int i,j; 22 23 memset(c,0,sizeof(c)); 24 25 for(i=0; i<N; i++) 26 27 for(j=0; j<N-i; j++) /*y<N-i: 确保i+j不会越界*/ 28 29 c[i+j] += a[i]*b[j]; 30 31 }
1 计算G(x)的前N项系数 2 3 /*计算出前N项系数!即g(x,1) g(x,2)... g(x,n)的展开结果*/ 4 5 void Init() 6 7 { 8 9 int i,k; 10 11 memset(a,0,sizeof(a)); 12 13 memset(c,0,sizeof(c)); 14 15 for(i=0;i<N;i++) a[i]=1; /*第一个多项式:g(x, 1) = x^0 + x^1 + x^2 + x^3 + */ 16 17 for(k=2;k<N;k++) 18 19 { 20 21 memset(b,0,sizeof(b)); 22 23 for(i=0;i<N;i+=k) b[i]=1;/*第k个多项式:g(x, k) = x^0 + x^(k) + x^(2k) + x^(3k) + */ 24 25 Poly(); /* 多项式乘法:c= a*b */ 26 27 memcpy(a,c,sizeof(c)); /*把相乘的结果从c复制到a中:c=a; */ 28 29 } 30 31 }
通过以上的代码,我们就计算出了G(x)的展开后的结果,保存到数组c中。此时有:f(n,n)=c[n];剩下的工作只是把相应的数组元素输出。