NYOJ 整数划分(三) (划分数大集合)

题意:虽然是中文题意但是还是想上一下题

题目描述

整数划分是一个经典的问题。请写一个程序,完成以下要求。

输入

每组输入是两个整数n和k。(1 <= n <= 50, 1 <= k <= n)

输出

对于输入的 n,k;
第一行: 将n划分成若干正整数之和的划分数。
第二行: 将n划分成k个正整数之和的划分数。
第三行: 将n划分成最大数不超过k的划分数。
第四行: 将n划分成若干个 奇正整数之和的划分数。
第五行: 将n划分成若干不同整数之和的划分数。

第六行: 打印一个空行

思路: 我们一个一个来看啊

(一)

将n划分成若干个正整数之和: 

我们设dp[i][j]表示的是,当前的数字是i,且最大的划分是j的时候的划分数

初始化:dp[][1] = 1, dp[1][] = 1 , 当一个数的最大划分是1的时候也就是他全部都是1,或者当前数字是1的时候只能由一种划分

转移方程

    dp[i][j] = dp[i][j-1] + dp[i-j][j](i>=j) 

    dp[i][j] = dp[i][i] (i<j) 

解释一下这个转移方程,当前数字为i最大划分是j可以由当前数字为i最大划分为j-1转换过来,(4,3) {1,3},{2,2}, (4,2) {1,2,1,},{2,2}是吧其实就是等于把那个大的在划分一下,还可以由i-j 和 j转移过来 (6,3) {3,3} (9,3) {3,3,3},其实就是前一个状态在加上一个j得来的。大概就是这个意思把。

(二)

将n划分成k个正整数之和的划分数(这个其实就是给你n个苹果和m个盘子而且盘子不能为空,问你最多有多少种分发)

我们设dp[i][j] 表示的是 当前还剩下n个苹果和m个盘子的时候的最大值

初始化: dp[][1] = 1,当他只剩下一个盘子的时候,就只有一种分法。

状态转移方程

dp[i][j] = dp[i-j][j] + dp[i-1][j-1] (i>=j) 

解释一下这个转移方程,你剩下i个苹果m个盘子的时候,那么下一次你放的时候,可以在每个盘子里都放一个苹果,他的转移方程就是dp[i-j][i],当前剩下i-j个苹果(因为每个盘子都放了一个,总共有j个盘子,之后盘子数量还是那么多)或者你只在一个盘子里面放一个苹果,之后就不管这个盘子了,那么他是dp[i-1][j-1],放了一个苹果所以i-1 ,丢了一个盘子j-1。

(三)

将n划分成不超过k的最大划分数

这个其实和第一个是一样的,状态转移方程也一样,这是返回值不一样而已,你仔细看看第一个对于dp数组的定义其实就是这个东西,不多说了好吧

(四)

将n划分成为几个奇正整数之和的划分数

我们设dp[i][j] 表示的是i这个数划分的最大值不超过k的时候的划分数,其实和第一个差不多就是这里有一个限制就是要是奇数

初始化:dp[][1] = 1 ;

转移方程:

当j为奇数的时候

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

dp[i][j] = dp[i][i];

当j为偶数的时候

dp[i][j] = dp[i][j-1]

解释一下转移方程,当他是奇数的时候,其实和第一个是一样的,但是只是奇数的时候才是一样的,当他是偶数的时候,我们知道j-1 是奇数,所以他是由dp[i][j-1] 转移过来的。

(五)

将n划分成几个不相同的正整数之和。

我们设dp[i][j]表示的是n这个数划分的最大值不超过k的划分数

初始化:

dp[][1] = 1; dp[1][] = 1,当他的最大划分为1的时候也就是说所有数都是1方案数是1,当他是1的时候,方案数也是1

转移方程 :

dp[i][j] = dp[i-j][j-1] + dp[i][j-1] (i>=j)

dp[i][j] = dp[i][i] (i<j)

解释一下转移方程,他说的是不能重复,所以当前数字是i,最大划分为j的时候,可以划分出一个j,又因为他不能重复,所以只能划分出一个j,划分完就不能用了,所以是dp[i-j][j-1](划分完了),其他的都一样,他可以把j在划分一个1

总结完了上代码把(感觉自己的认知还是不深刻。。。要在看一下别人的博客呢)

 

#include <bits/stdc++.h>
using namespace std;
#define mes(a) memset(a,0,sizeof(a));
int n,k;
int a()//将n划分成若干正整数之和的划分数
{
	int dp[55][55];
	mes(dp);
	dp[0][0] = 1;
	for(int i = 0 ; i < 55;i++)
	{
		for(int j = 1 ; j < 55; j++)
		{
			if(i>=j)
			{
				dp[i][j] = dp[i-j][j] + dp[i][j-1];
			}
			else 
			{
				dp[i][j] = dp[i][i];
			}
		}
	}
	return dp[n][n];
}
int b()//将n划分成k个正整数之和的划分数。
{
	int dp[55][55];
	mes(dp);
	dp[1][1] =1;
	for(int i = 2 ; i <= 51 ; i++)
	{
		for(int j = 1 ; j <= i ; j++)
		{
			dp[i][j] = dp[i-1][j-1]+dp[i-j][j];
		}
	}
	return dp[n][k];
	
}
int c()//将n划分成若干正整数之和的划分数
{
	int dp[55][55];
	mes(dp);
	dp[0][0] = 1;
	for(int i = 0 ; i < 55 ; i++)
	{
		for(int j = 1 ; j < 55 ; j++)
		{
			if(i>=j)
			{
				dp[i][j] = dp[i-j][j] + dp[i][j-1];
			}
			else 
			{
				dp[i][j] = dp[i][i];
			}
		}
	}
	return dp[n][k];
}
int d()
{
	int dp[55][55];
	mes(dp);
	for(int i = 0 ; i <  55 ; i++)
	{
		dp[i][1] = 1;
		if(i&1)
		{
			dp[0][i] = 1;
		}
	}
	for(int i = 1 ; i < 55 ; i++)
	{
		for(int j = 1 ; j < 55 ; j++)
		{
			if(j&1)
			{
				if(i>=j)
				{
					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];
			}
		}
	}
	return dp[n][n];
}
int e()
{
	int dp[55][55];
	mes(dp);
	dp[0][0] = 1;
	for(int i = 0 ; i < 55 ; i++)
	{
		for(int j = 1 ; j < 55 ; j++)
		{
			if(i>=j)
			{
				dp[i][j] = dp[i-j][j-1] + dp[i][j-1];
			}
			else 
			{
				dp[i][j] = dp[i][i];
			}
		}
	}
	return dp[n][n];
}
int main()
{
	while(cin>>n>>k)
	{
		printf("%d\n%d\n%d\n%d\n%d\n",a(),b(),c(),d(),e());
		puts("");
	}
}




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值