其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不
同划分个数。
例如正整数6有如下11种不同的划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
-
输入
- 第一行是测试数据的数目M(1<=M<=10)。以下每行均包含一个整数n(1<=n<=10)。 输出
- 输出每组测试数据有多少种分法。 样例输入
-
1 6
样例输出
11
此题可以用递归和动态规划两种方法来解决,首先介绍动态规划的,数组dp[N][M]表示N为被划分数,M为划分数的最大值,此题M==N,故即求dp[N][N];
1>状态转移方程:
dp[N][M]=dp[N][M-1]+dp[N-M][M];
该怎样理解呢?这里分两步:
Step 1:所划分的最大数不包括M,即每个划分数都是小于M的,此时总数为dp[N][M-1].
Step 2:所划分的最大数包括M,那么这一步被划分数就应该减去一个M,此时总数为dp[N-M][M].
到这里就是完整的思路了,应该注意的是上面的划分,划分数里有重复的数,那么如果要求划分数没有重复的呢,该怎样求呢?
这里的状态转移方程和上面就有点细微区别了.先来看看方程:
2>dp[N][M]=dp[N][M-1]+dp[N-M][M-1];
其实联系1中的步骤就不难理解了,同样分为两步:
Step 1:所划分的最大数不包括M,即每个划分数都是小于M的,此时总数也是dp[N][M-1].
Step 2:所划分的最大数包括M,那么划分就的相应的减去M,注意到不能重复,即M划分数出现的次数只能为1.所以M就得换成M-1了,即dp[N-M][M-1].
3>在拓展一下,要是划分的个数为确定的数呢?即dp[N][K].表示N被划分成K个数.
这时状态转移方程就为
dp[N][K]=dp[N-K][K]+dp[N-1][K-1].
应该这样理解:
Step 1:被划分的K个数中不包括1,那么就应该先自动的为其分配1,K个数共N-K,剩下的数自由分配,总能保证其值大于2,即dp[N-K][K].
Step 2:存在一个数为1的情况,此时剩下的N-1分给K-1个数,即dp[N-1][K-1].
下面来介绍递归的方法:
dp[N][M]同上,首先来讨论下临界条件.
1.如果M==1,即划分的数为1,那么只要N>0,它所划分的情况恒为一种,即全为1.
2.如果N<1或者M<1,可以看成不符合正整数,即此时为0.
3.如果N==1,同理如果M>=1,它也只有一种划分,即1.
4.如果N<M.假设dp[6][8],因为7,8不可能最为划分数,所以dp[6][8]等价dp[6][6].
即dp[N][M]=dp[N][N] (N<M)
5.如果N==M,可以这样理解除去划分数为其本身是一种情况,其余的划分划分数必定小于N,所以
dp[N][M]=dp[N][M-1]+1.
6.如果N>M,分两种情况
1.如果划分数中存在M,可以假设(N=m+x0+x1+x2...)即此情况等价于求x0+x1+x2..的划分,即
dp[N-M][M];
2.如果划分数不存在M,那么此情况等于N个数的最大划分为M-1,即dp[N][M-1].
2种ACcode:
1.递归:
#include<iostream>
#include<stdio.h>
using namespace std;
int Divide(int n,int m)
{
if(n==1||m==1) return 1;
if(n<1||m<1) return 0;
if(m>n) return Divide(n,n);
if(n==m) return 1+Divide(n,m-1);
if(n>m) return Divide(n-m,m)+Divide(n,m-1);
}
int main()
{
int test,n,k;
scanf("%d",&test);
while(test--)
{
scanf("%d",&n);
printf("%d\n",Divide(n,n));
}
}
2.DP
#include<stdio.h>
#include<iostream>
using namespace std;
#define N 52
int dp[N][N];
void Init(int n)
{
for(int i=1;i<=n;++i)
{
dp[1][i]=1;
dp[i][1]=1;
dp[0][i]=0;
dp[i][0]=0;
}
}
int main()
{
int test,n,i,j;
scanf("%d",&test);
while(test--)
{
scanf("%d",&n);
Init(n);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
{
if(i>j)
dp[i][j]=dp[i][j-1]+dp[i-j][j];
if(i==j)
dp[i][j]=1+dp[i][j-1];
if(i<j)
dp[i][j]=dp[i][i];
}
cout<<dp[i-1][j-1]<<endl;
}
}