您的位置:首页 > 运维架构

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

2016-07-19 01:15 447 查看

03:复杂的整数划分问题

总时间限制: 200ms 内存限制: 65536kB
描述

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

这道题有三个问题,一是有个数限制的整数划分,二是不同数的划分,三是划分成奇数

在考虑有限个数整数划分时,我们可以设状态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<cstdio>
#include<iostream>
#include<cstring>
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
[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
}

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

);
}

int main()
{
while(~scanf("%d%d",&N,&K))
{
work1();
work2();
work3();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: