描述:将一个正整数n拆分成一组正整数连加并等于n的形式,而且这些连加的数中最大的加数不大于n。
正整数的划分数:上述划分所有可能的个数。
求正整数划分数的方法:
递归方法:
假设m为一种划分中的最大加数,如果m>n,则最大加数为n。
共分为四种情况讨论(其实可以看成三种):
- 当n=1或m=1时,此时对正整数n只有一种划分方式,即当n=1时,划分为n=1; 当m=1时,n=1+1+...+1, 其中1的个数为n的大小。此时的划分数为1,用程序表示为 if(n==1||m==1) return 1;
- 当m>n时,假设递归函数为int split(int n,int m),由于最大加数只能为n,此时可以等价于split(n,n),用程序可以表示为if(m>n) return split(n,n);
- 当m=n时,可以用递归形式表示为split(n,m-1)+1,即包含加数为m的划分数加上最大加数为m-1的划分数,由于此时n=m,所以包含加数为m的划分数为1,用程序可以表示为if(m==n) return split(n,m-1)+1;
- 当m<n时,这是最一般的情况,也是出现最多的情况。此时可以表示为split(n,m-1)+split(n-m,m),其中split(n-m,m)表示用最大加数为m的划分表示n-m。用程序表示为if(m<n) return split(n,m-1+split(n-m,m)。
#include <iostream>
using namespace std;
int split(int n,int m)
{
if(n<1||m<1) return 0;//invalid arguments
if(n==1||m==1) return 1;
if(m>n) return split(n,n);
if(m==n) return split(n,m-1)+1;
if(m<n) return split(n,m-1)+split(n-m,m);
}
int main()
{
int split_number,add_number;
cin>>split_number;
cin>>add_number;
cout<<split(split_number,add_number)<<endl;
return 0;
}
另:将一个正整数划分为连续正整数之和,输出所有的可划分情况。
假设正整数为n,一个满足条件的划分中最小的正整数为x,那么这个划分可以表示为x+(x+1)+(x+2)+..+(x+m)=n,假设这个划分中整数的个数为y个,那么上式可以表示为(x*y)+y*(y-1)/2=n。
我们知道这样的划分中整数的个数可以为y个,那么y的最大值是多少呢?即一个划分中可以包含的连续整数的最大个数是多少呢?
假如一个划分从整数1开始,那么这个划分包含的整数个数将是最大的,由此可得y+y(y-1)/2=n => y(y+1)/2=n, 此即最大个数y应该满足的条件。
同样在确定y值的时候,可以根据式xy+y(y-1)/2=n来确定x的值,这个值就是连续整数中最小的整数值,当然若(n-y(y-1)/2)/y不是整数,那么就不能将整数n划分成包含y个的连续整数的和。
C++实现的划分函数如下:
int split(int n)
{
int i, j, m = 0, x, t1, t2;
// 在这里i + 1之所以变为i - 1,是因为i * (i - 1) / 2这个式子在下面多次用到,
// 为了避免重复计算,因此将这个值计算完后保存在t1中。并且将<= 号变为了<号。
for(i = 1; (t1 = i * (i - 1) / 2) < n; i++)
{
t2 = (n - t1);
x = t2 / i;
if(x <= 0) break;
if((n - t1) % i == 0)
{
cout<<x<<' ';
for(j = 1; j < i; j++)
cout<< x + j<<' ';
cout<<endl;
m++;
}
}
return m;
}