先说明一下问题,什么是整数划分?
- n=m1+m2+...+mi; (其中mi为正整数,并且1 <= mi <= n),则{m1,m2,...,mi}为n的一个划分。
- 如果{m1,m2,...,mi}中的最大值不超过m,即max(m1,m2,...,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
- 举个例子,当n=5时我们可以获得以下这几种划分(注意,例子中m>=5)
5 = 5
= 4 + 1
= 3 + 2
= 3 + 1 + 1
= 2 + 2 + 1
= 2 + 1 + 1 + 1
= 1 + 1 + 1 + 1 + 1
这里f(n,m)有以下几种不同的情况:
1. f(1, m) = 1,m>=1
当n=1,无论m取值多少,都只有一种
2.f(n, 1) = 1, n>=1
当m=1,无论n取值多少,都只有一种
3.f(n, m) = f(n, n),m>=n
当m大于等于n时,因为加数不可能大于n,如f(3,5)=f(3,3)
4.f(n, n) = f(n, n-1) -1
正整数n的划分是由s=n的划分和s<=n-1的划分构成。例如f(6,6) = 1+f(6,5)。
5.f(n, m) = f(n, m-1) + f(n-m, m), n>m>1
正整数n的最大加数不大于m的划分,是由s=m的划分和s<=m-1的划分组成。
例如f(6, 4) = f(6, 3) + f(2, 4) = f(6, 3) + f(2, 2)
综合以上情况,我们可以看出,上面的结论具有递归定义特征,其中(1)和(2)属于回归条件,(3)和(4)属于特殊情况,将会转换为情况(5)。而情况(5)为通用情况,属于递推的方法,其本质主要是通过减小m以达到回归条件,从而解决问题。其递推表达式如下:
- f(n, m)= 1; (n=1 or m=1)
- f(n, m)=f(n, n); (n<m)
- 1+ f(n, m-1); (n=m)
- f(n-m,m)+f(n,m-1); (n>m)
#include <cstdio>
#include <iostream>
using namespace std;
int split(int n,int m)
{
if(n==1||m==1)
return 1;
else if(n<m)
return split(n,n);
else if(n==m)
return split(n,n-1)+1;
else return split(n,m-1)+split(n-m,m);
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
cout<<split(n,n)<<endl;
}
return 0;
}
//https://nanti.jisuanke.com/t/25093
/*
思路:
用f(n,m)表示把n分解成不多于m个正整数
那么有以下几种情况:
1.如果n或m等于1,则f(n,m)=1;
2.如果m>n,则f(n,m)=f(n,n);
3.如果m==n,则f(n,m)=f(n,m-1)+1;
4.一般情况,f(n,m)=f(n-m,m)+f(n,m-1);
*/
/* 递归超时
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
ll split(int n,int m)
{
if(n==1||m==1)
return 1;
else if(n<m)
return split(n,n);
else if(n==m)
return split(n,n-1)+1;
else return split(n,m-1)+split(n-m,m);
}
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)!=EOF)
{
printf("%lld\n",split(n,k));
}
return 0;
}
*/
//用动态规划复杂度明显低了很多
#include<iostream>
using namespace std;
long long a[400][400];//a[n][k]表示将数n,分成k份,每份可以是任意值
int main()
{
long long n,k;
cin>>n>>k;
for (int i=1;i<=n;i++)
{
a[i][1]=1;//将i分成1份,只有一种分法
}
for (int j=1;j<=k;j++)
{
a[1][j]=1;//将1分成j,有一种
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=k;j++)
{
//当i<j时,因为此时最多分成i份,实际上相当于将i分成i份
if(i<j)
a[i][j]=a[i][i];
//当i==j时,分两种情况,一种是每份分1,
//只有一种分法,第二种至少有一份为0,此时相当于a[i][j-1]
else if (i==j)
a[i][j]=a[i][j-1]+1;
//当m>n时,也分两种情况,一种是至少有一份为0,
//相当于a[i][j-1],第二种,先将j分出来,然后将i-j再分成j份,此时相当于a[i-j][j];
else if (i>j)
a[i][j]=a[i-j][j]+a[i][j-1];
}
}
cout<<a[n][k];
system("pause");
return 0;
}