整数划分 NYOJ 90

描述
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,
其中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;
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值