OpenJudge 7219 复杂的整数划分问题(dp)

描述

将正整数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

问题1

N划分成K个正整数之和的划分数目
f[i][j]将i划分为j个正整数的划分数目
如果i=j,只能全部划分为1,只有1种
如果i<j,无法划分
如果i>j,可以分为有1和没有1。有1的话,拿出去一个1,需要划分的数i减1,划分的份数也要减1,也就是f[i-1][j-1];如果没有1,也就是说划分出来的每一份都比1大,那么就将每一份都减去1,i就变成了i-j,划分的份数j不变,即f[i-j][j]
转移方程 f[i][j]=f[i-j][j]+f[i-1][j-1]

void work1()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i&&j<=k;j++)
		{
			if(i>j)
				f[i][j]=f[i-j][j]+f[i-1][j-1];
			if(i==j)
				f[i][j]=1;
		}	
	cout<<f[n][k]<<endl;
}

问题2

N划分成若干个不同正整数之和的划分数目
可以先看看分成若干个可以相同的正整数划分数目
f[i][j]表示将i分成若干个不同且不大于j的正整数的划分数目
可以分为有j这个是和没有
有的话,那么剩下需要分的数就为i-j,由于不能有相同的数,那么就不能是不大于j了,需要变成不大于j-1,即f[i-j][j-1]
没有的话,那么就需要将i-1划分为j个数,即f[i-1][j]
转移方程 f[i][j]=f[i][j-1]+f[i-j][j-1]

void work2()
{
	memset(f,0,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		f[0][i]=1;
		f[i][1]=0;
	}
	f[1][1]=1;
	for(int i=1;i<=n;i++)
		for(int j=2;j<=n;j++)
		{
			if(j<=i)
				f[i][j]=f[i][j-1]+f[i-j][j-1];
			else
				f[i][j]=f[i][i];
		}
	cout<<f[n][n]<<endl;
}

问题3

N划分成若干个奇正整数之和的划分数目
f[i][j]表示为将i分为j个奇数的划分数目 g[i][j]表示为将i分为j个偶数的划分数目
可以分为有1和无1两种情况
有1,那么需要分的数需要减1,份数减1,即f[i-1][j-1]
无1,那么就将没一份都减1,分的数就为i-j,份数仍j,每一份都减了1.奇数减1变为偶数,所求就变成了将i-j分为j个偶数,即g[i-j][j]
转移方程 f[i][j]=g[i-j][j]+f[i-1][j-1]

void work3()
{
	memset(f,0,sizeof(f));
	f[0][0]=1;
	g[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++)
		{
			g[i][j]=f[i-j][j];
			f[i][j]=g[i-j][j]+f[i-1][j-1];
		}
	int s=0;
	for(int i=1;i<=n;i++)
		s+=f[n][i];
	cout<<s<<endl;
}

代码

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

int f[51][51],g[51][51];
int n,k;

void work1()
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i&&j<=k;j++)
		{
			if(i>j)
				f[i][j]=f[i-j][j]+f[i-1][j-1];
			if(i==j)
				f[i][j]=1;
		}	
	cout<<f[n][k]<<endl;
}

void work2()
{
	memset(f,0,sizeof(f));
	for(int i=1;i<=n;i++)
	{
		f[0][i]=1;
		f[i][1]=0;
	}
	f[1][1]=1;
	for(int i=1;i<=n;i++)
		for(int j=2;j<=n;j++)
		{
			if(j<=i)
				f[i][j]=f[i][j-1]+f[i-j][j-1];
			else
				f[i][j]=f[i][i];
		}
	cout<<f[n][n]<<endl;
}

void work3()
{
	memset(f,0,sizeof(f));
	f[0][0]=1;
	g[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++)
		{
			g[i][j]=f[i-j][j];
			f[i][j]=g[i-j][j]+f[i-1][j-1];
		}
	int s=0;
	for(int i=1;i<=n;i++)
		s+=f[n][i];
	cout<<s<<endl;
}

int main()
{
	while(cin>>n>>k)
	{
		work1();
		work2();
		work3();
	}
	return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值