openjudge整数划分问题

1:复杂的整数划分问题

总时间限制: 
200ms
内存限制: 
65536kB
描述

将正整数表示成一系列正整数之和,n=n1+n2+…+nk, 其中n1>=n2>=…>=nk>=1 k>=1 
正整数的这种表示称为正整数的划分。

输入
标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数N 和 K。 
(0 < N <= 50, 0 < K <= N)
输出
对于每组测试数据,输出以下三行数据:
第一行: N划分成K个正整数之和的划分数目
第二行: N划分成若干个不同正整数之和的划分数目
第三行: N划分成若干个奇正整数之和的划分数目
样例输入
5 2
样例输出
2
3
3
提示
第一行: 4+1, 3+2,
第二行: 5,4+1,3+2

第三行: 5,1+1+3, 1+1+1+1+1+1

当对划分整数没有限制,dp[i][j]表示整数i,不超过

dp[i][j] = 1 + dp[i][j-1]   when   i=j;

dp[i][j]=dp[i-j][j]+dp[i][j-1]  when  j<i;

dp[i][1] = 1

dp[1][i] = 1

当j>i dp[i][j]=dp[i][i];

当要求划分整数必须不同时,同样的有

dp[i][1]=0,dp[1][i]=1

dp[i][j] = 1 + dp[i][j-1]   when   i=j;

dp[i][j]=dp[i-j][j-1]+dp[i][j-1]  when  j<i;

要求N个整数分为k个整数之和,这个是有公式的

dp[i][j]为将i划分为j个整数的划分数。

  (1) i<j为不可能出现的情况,dp[i][j]=0

  (2) 若i=j,有一种情况:i可以划分为i1之和,dp[i][j]=1

  (3) 若i>j,可以根据划分数中是否含有1分为两类:若划分数中含有1,可以使用“截边法”将j个划分分别截去一个1,把问题转化为i-jj-1个划分数,为dp[i-j][j-1]; 若划分中不包含1,使用“截边法”将j个划分数的最下面一个数截去,将为题转化为求i-jj个划分数,为dp[i-j][j]。所以i>jdp[i][j]=dp[i-j][j-1]+dp[i-j][j]


将n化为若干奇数之和

f[i][j]为将i划分为j个奇数之和的划分数,g[i][j]为将i划分为j个偶数之和的划分数。

使用截边法,将g[i][j]j个划分都去掉1,可以得到f[i-j][j],所以

g[i][j] = f[i-j][j]

f[i][j]中有包含1的划分方案和不包含1的划分方案。对于包含1的划分方案,可以将1的划分除去,转化为“将i-1划分为j-1个奇数之和的划分数”,即f[i-1][j-1];对于不包含1的划分方案,可以使用截边法对j个划分每一个都去掉一个1,转化为“将i-j划分为j个偶数之和的划分数”,即g[i-j][j]

所以f[i][j]=f[i-1][j-1]+g[i-j][j]

f[n][0]+f[n][1]+……+f[n][n]为将n划分为若干奇数的划分数,为问题4的答案。


#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

#define MAXN 50
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;

int N,K;
LL dp[MAXN+10][MAXN+10];

void work1()//选K个
{
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=N;++i)//将i分成1个数只有一种方案
		dp[i][1]=1;
	
	for(int i=1;i<=N;++i)
        for(int j=2;j<=i;++j)//将每个数统一减1,或去掉当前数中的1
			dp[i][j]=dp[i-j][j]+dp[i-1][j-1];
	
	printf("%d\n",dp[N][K]);//把N分成K个数
}

void work2()//任意不同
{
	memset(dp,0,sizeof(dp));  
	dp[0][0]=1;  
	
	for(int i=0;i<=N;i++)
	{
        for(int j=1;j<=N;j++)
		{					//当前有数是j和降低上限
			if(j<=i)dp[i][j]=dp[i-j][j-1]+dp[i][j-1];
			else dp[i][j]=dp[i][i];//上限应为i
		}
	}
	
	printf("%lld\n",dp[N][N]);//划分N,上限为N
}

void work3()//任意奇数(基本同work1)
{
	memset(dp,0,sizeof(dp));
	for(int i=0;i<=N;++i)
	{
		dp[i][1]=1;		
		if(i&1)dp[0][i]=1;//预处理第0层
	}
	
	for(int i=1;i<=N;i++)
	{
		for(int j=1;j<=N;j++)
		{
			if(j&1)//同work1
			{
				if(j<=i)dp[i][j]=dp[i-j][j]+dp[i][j-1];
				else dp[i][j]=dp[i][i];
			}
			else dp[i][j]=dp[i][j-1];//当前非奇数
		}
	}
	
	printf("%lld\n",dp[N][N]);
}

int main()
{
	while(~scanf("%d%d",&N,&K))
	{
		work1();
		work2();
		work3();
	}
	
}


猜你喜欢

转载自blog.csdn.net/wwxy1995/article/details/80287272