001:复杂的整数划分问题

描述

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

输入

标准的输入包含若干组测试数据。每组测试数据是一行输入数据,包括两个整数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

解题思路

  1. N划分成K个正整数之和的划分数目
    dp[i][j] 代表整数 i 拆成 j 个正整数有多少种拆法,那么 dp[i][j] = dp[i-j][j] + dp[i-1][j-1] 分成两种情况 
    1. 拆分后的正整数中没有1 那么就等价于 dp[i-j][j] 相当于 每个数都已经分配了 1 然后剩余 i - j 再来拆成 j 个正整数
    2. 拆分后的正整数中至少有一个1  dp[i-1][j-1]

    边界条件  dp[i][1]=1 dp[i][i]=1

  2. N划分成若干个不同正整数之和的划分数目
    dp[i][j] 表示从前 i 个数中挑选若干数凑成 j 的选法,那么 dp[i][j] = dp[i-1][j-i] + dp[i-1][j]
    1. dp[i-1][j-i] 选第 i 个数 再从前面 i - 1 个数中选若干个凑出 j - i
    2. dp[i-1][j] 不选第 i 个数 从前面 i - 1 个数中选出若干个凑出 j 
  3. N划分成若干个奇正整数之和的划分数目
    dp[i][j] 代表整数 i 拆成 j 个奇正整数有多少种拆法,那么 dp[i][j] = dp[i-2*j][j] + dp[i-1][j-1]
    1. 同第一种情况 拆分后的正整数中没有1 那么就等价于 dp[i-2*j][j] 相当于 每个数已经分配了2 然后剩余 i - 2*j 再来拆成 j 个正奇数
    2. 拆分后的正奇数中至少有一个1 dp[i-1][j-1]

         边界条件  如果 i 是奇数 那么 dp[i][1] = 1 如果 i == j 那么 dp[i][j] = 1;

AC Code

#include<iostream>
#include<cmath>
#include<stack>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const double PI=acos(-1);
const double EPS=1e-6;
const int MAXN=100+10;
int n,k;
int dp1[MAXN][MAXN];//dp1[i][j]表示将整数i拆成j个正整数的拆法 
int dp2[MAXN];//dp2[j]凑成j的不同选法 (滚动数组)
int dp3[MAXN][MAXN];//dp3[i][j]表示将整数i拆成j个奇正整数的拆法 

int solution1(){
	memset(dp1,0,sizeof(dp1));
	for(int i=1; i<=n; ++i){
		dp1[i][1]=1; 
		for(int j=1; j<=k; ++j){
			if(i==j) dp1[i][j]=1;
			else if (i>=j) dp1[i][j]=dp1[i-j][j]+dp1[i-1][j-1];//不包含1的情况和包含1的情况 
		}
	}
	return dp1[n][k];
}

int solution2(){
	memset(dp2,0,sizeof(dp2));
	for(int i=1; i<=n; ++i){
		for(int j=n; j>=0; --j){
			if(j==1||j==0) dp2[j]=1;
			else if (j>=i) dp2[j]=dp2[j-i]+dp2[j];//选i和不选i的情况 
//			printf("dp[%d]=%d ",j,dp2[j]);
		}
//		printf("\n");
	} 
	return dp2[n];
}

int solution3(){
	memset(dp3,0,sizeof(dp3));
	for(int i=1; i<=n; ++i){
		if(i%2) dp3[i][1]=1;
		for(int j=1; j<=n; ++j){
			if(i==j) dp3[i][j]=1;
			else if (i>=2*j) dp3[i][j]=dp3[i-2*j][j]+dp3[i-1][j-1];
			else dp3[i][j]=dp3[i-1][j-1];
		}
	}
	int cnt=0;
	for(int i=1; i<=n; ++i)
		cnt+=dp3[n][i];
	return cnt; 
}

int main(){
    freopen("C:\\Users\\Ambition\\Desktop\\in.txt","r",stdin);
    freopen("C:\\Users\\Ambition\\Desktop\\out.txt","w",stdout);
    while(~scanf("%d%d",&n,&k)){
    	printf("%d\n%d\n%d\n",solution1(),solution2(),solution3());
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值