[整数的分划问题]
对于一个正整数M的分划就是把M写成一系列正整数之和的表达式。例如,对于正整
数M=6,它可以分划为:
6
5+1
4+2, 4+1+1
3+3, 3+2+1, 3+1+1+1
2+2, 2+2+1+1, 2+1+1+1+1
1+1+1+1+1+1
注意,分划与顺序无关,例如5+1和1+5是同一种分划。另外,这个整数M本身也算
是一种分划。现在的问题是,对于给定的正整数M,要求编程序计算出其分划的数
目P(M)。
分析:
从上面n=6的例子可以看出,很难找到大规模问题P(M)与小规模问题P(n-d) (d=1,2,3...)的关系。
根据n=6可以发现:第一行及以后的数据不超过6,第二行及以后的数据不超过5,。。。第六行及以后的数据不超过1。
解析:
我们定义一个函数Q(n,m),表示整数n的“任何被加数都不超过m”的分划的数目。
Q(n,m)有以下递归关系:
(1)Q(n,n)=1+Q(n,n-1)
等式右边的1表示“n只包含一个被加数等于n本身的划分;则其余的分划表示n的所有其他划分,即最大加数m<=n-1的划分。
(2)Q(n,m)=Q(n,m-1)+Q(n-m,n) (m<n)
等式右边的第一部分表示被加数中不包含m的分划的数目;第二部分表示被加数中包含(注意不是小于)m的分划的数目,因为如果确定了一个分划
的被加数中包含m,则剩下的部分就是对n-m进行不超过m的划分。
到此找到了大规模问题与小规模问题的递归关系,下面是递归的停止条件:
(1)Q(n,1)=1,表示当最大的被加数是1时,该整数n只有一种划分,即n个1相加;
(2)Q(1,m)=1,表示整数n=1只有一种划分,不管最大被加数的上限m是多大。
有了递归关系式后,解决问题的算法就非常简单了:
Partition(M, N)
1. if M < 1 or N < 1
2. then Error("输入参数错误")
3. else if M = 1 or N = 1
4. then return 1
5. else if M < N
6. then return Partition(M, M)
7. else if M = N
8. then return (1 + Partition(M, M-1))
9. else
10. return (Partition(M, N-1) + Partition(M-N, N));
请注意,正整数的划分的数目随着M的增加增长的非常快(以指数级增长),所以
不要用较大的整数来测试按照上述算法编写出的程序。
代码如下:
#include <stdio.h> int Divide(int n,int m); int main() { int n; scanf("%d",&n); printf("%d\n",Divide(n,n)); return 0; } int Divide(int n,int m) { if (n<1||m<1) { printf("Error\n"); } else if (n==1||m==1) { return 1; } else if (n<m) { return Divide(n,n); } else if (n==m) { return 1+Divide(n,n-1); } else { return Divide(n,m-1)+Divide(n-m,m); } }