Openjudge 百练 03:复杂的整数划分问题

03:复杂的整数划分问题


总时间限制: 
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

这道题有三个问题,一是有个数限制的整数划分,二是不同数的划分,三是划分成奇数
在考虑有限个数整数划分时,我们可以设状态dp[i][j],表示将整数i划分成j个整数

那么就有dp[i][j]=dp[i-j][j]+dp[i-1][j-1]。

dp[i-j][j]有j个整数(都不为1),每个整数都同时减1。

dp[i-1][j-1]表示当前构成i的整数中有1,去掉里面的1。


至于划分为不重复整数和奇数,既可以枚举整数或奇数做0-1背包,也可以通过第一个问题的方法转移 dp[i][j]=dp[i-j][j-1]+dp[i][j-1]; | j<=i;(上限合适)->划分成的数里有j+划分成的数里没有j
dp[i][j]=dp[i][i];                        | j>i (上限过大)

只是奇数做DP时要注意枚举情况
#include
#include
#include
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();
	}
	
}


你可能感兴趣的:(动态规划)