Nyoj 571 整数划分

103 篇文章 1 订阅
62 篇文章 0 订阅

  第一行:将n划分成若干个正整数之和的划分数

       我们设dp[i][j] 表示将正整数i划分成最大数不超过数j的划分数,显然我们有:

       (1):如果划分的数中至少有一个有j,则有:dp[i][j] = dp[i-j][j];

       (2):如果划分的数中没有j,则有:dp[i][j] = dp[i][j-1];

       因此有:dp[i][j] = dp[i-j][j] + dp[i][j-1];

  借助此问可得出第三问的结果:第三行: 将n划分成最大数不超过k的划分数。dp[n][k]

  第二行: 将n划分成k个正整数之和的划分数。

   首先,正确理解“整数n分成k份,这k个整数不考虑顺序的含义”指的是同一种划分与k个整数的排列无关,例如下面5种8的4划分看作是同一种划分法:

1+2+2+3;2+2+3+1;1+3+2+2;3+2+1+2 ;2+2+1+3。

因此将n划分份的一种方法唯一的表示为n1+n2+……nk,其中n1<=n2<=….nk.

       这样可以形象地把n的k份划分看作是把n块积木堆成k列,且每列的积木块数依次递增,也就是这n块积木从左到右被堆成了“阶梯状”。比如,下图是5的2种划分方法:

把上图的两个矩形顺时针旋转90度后,如下图:

 

 

 

不难发现,选转之后的模型还是5的划分,不过约束条件有所不同。很明显,由于原来是k份划分,因此新的模型中的最大一个元素必然是k。而其余的元素大小不限,但都不能大于k.  n减去k后,n1=n-k, 剩下的问题就是求n1的任意划分,且其中每个元素都不大于k的方案总数了。即转化为:dp[n-k][k]

 

 

   第四行:将n划分成若干奇正整数之和的划分数。
dp1[i][j]是当前的划分数为i,最大值为j时的中的划分数,则状态转移方程为dp1[i][j] = 

   第五行: 将n划分成若干不同整数之和的划分数。

      1:划分数中出现最大数j,则:dp2[i][j] = dp2[i-j][j-1]

      2:划分数中没有出现最大数j,则:dp2[i][j] = dp2[i][j-1],因此:

    dp2[i][j] = dp2[i][j-1] + dp2[i-j][j-1]

 

#include <iostream>
#include <cstring>
#include <cstdio>


using namespace std;

const int MAXN = 55;

int dp[MAXN][MAXN];
int dp1[MAXN][MAXN];
int dp2[MAXN][MAXN];

void Pre_Solve1()
{
	int i, j;
	for (i = 1; i < MAXN; ++i)
	{
		for(j = 1; j < MAXN; ++j)
			if(i < j)
				dp[i][j] = dp[i][i];
			else
				dp[i][j] = dp[i][j-1] + dp[i-j][j];
	}

	return ;
}

void Pre_Solve2()
{
	int i, j;
	for (i = 1; i < MAXN; ++i)
	{
		for (j = 3; j < MAXN; j += 2)
		{
			if(i < j && i & 1)
				dp1[i][j] = dp1[i][i];
			else if(i < j && !(i & 1))
				dp1[i][j] = dp1[i][i-1];
			else if(i >= j)
				dp1[i][j] = dp1[i-j][j] + dp1[i][j-2];
		}
	}
	return ;
}

void Pre_Solve3()
{
	int i, j;
	for (i = 1; i < MAXN; ++i)
	{
		for (j = 1; j < MAXN; ++j)
		{
			if(i < j)
				dp2[i][j] = dp2[i][i];
			else
				dp2[i][j] = dp2[i-j][j-1] + dp2[i][j-1];
		}
	}

	return ;
}


int main()
{
	int i;
	int n, k;
	memset(dp, 0, sizeof(dp));
	memset(dp1, 0, sizeof(dp1));
	memset(dp2, 0, sizeof(dp2));
	for (i = 0; i < MAXN; ++i)
	{
		dp[0][i] = 1;
		dp1[i][1] = 1;
		dp2[0][i] = 1;
	}
	for (i = 1; i < MAXN; i += 2)
	{
		dp1[0][i] = 1;
	}
	dp1[0][0] = 0;

	Pre_Solve1();
	Pre_Solve2();
	Pre_Solve3();
	while (~scanf("%d %d", &n, &k))
	{
		printf("%d\n%d\n%d\n%d\n%d\n\n", dp[n][n], dp[n-k][k], dp[n][k], (n&1) ? dp1[n][n] : dp1[n][n-1], dp2[n][n]);
	}
	return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值