您的位置:首页 > 其它

题解:整数划分问题(DP)

2016-07-31 21:11 211 查看
总时间限制: 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划分成若干个不同正整数之和的划分数目

第四行: N划分成若干个奇正整数之和的划分数目

样例输入

5 2

样例输出

2

7

3

3

分析

一 求将n划分为若干正整数之和的划分数

若划分的多个整数可以相同

  设dp[i][j]为将i划分为不大于j的划分数

  (1) 当i < j 时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];

  (2) 当i > j 时,可以根据划分中是否含有j分为两种情况。若划分中含有j,划分方案数为dp[i-j][j];若划分数中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j]+dp[i][j-1];

  (3) 当i=j 时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]。

dp

可以解决问题1,dp
[k]表示将n划分为最大数不超过k的划分数,可以解决问题3。

若划分的正整数必须不同

  设dp[i][j]为将i划分为不超过j的不同整数的划分数

  (1) 当i < j时,i不能划分为大于i的数,所以dp[i][j]=dp[i][i];

  (2) 当i > j时,可以根据划分中是否含有j分为两种情况。若划分中含有j,则其余的划分中最大只能是j-1,方案数为dp[i-j][j-1];若划分中不含j,相当于将i划分为不大于j-1的划分数,为dp[i][j-1]。所以当i>j时dp[i][j]=dp[i-j][j-1]+dp[i][j-1];

  (3) 当i=j时,若划分中含有j只有一种情况,若划分中不含j相当于将i划分为不大于j-1的划分数。此时dp[i][j]=1+dp[i][j-1]

dp

表示将n划分为不同整数的划分数,可以解决问题5.

二 将n划分为k个整数的划分数

设dp[i][j]为将i划分为j个整数的划分数。

  (1) i < j为不可能出现的情况,dp[i][j]=0;

  (2) 若i = j,有一种情况:i可以划分为i个1之和,dp[i][j]=1;

  (3) 若i > j,可以根据划分数中是否含有1分为两类:若划分数中含有1,可以使用“截边法”将j个划分分别截去一个1,把问题转化为i-j的j-1个划分数,为dp[i-j][j-1]; 若划分中不包含1,使用“截边法”将j个划分数的最下面一个数截去,将为题转化为求i-j的j个划分数,为dp[i-j][j]。所以i>j时dp[i][j]=dp[i-j][j-1]+dp[i-j][j]。

dp
[k]为将n划分为k个整数的划分数,可解决问题2。

三 将n划分为若干正奇数之和的划分数

设f[i][j]为将i划分为j个奇数之和的划分数,g[i][j]为将i划分为j个偶数之和的划分数。

使用截边法,将g[i][j]的j个划分都去掉1,可以得到f[i-j][j],所以

g[i][j] = f[i-j][j]。

f[i][j]中有包含1的划分方案和不包含1的划分方案。

对于包含1的划分方案,可以将1的划分除去,转化为“将i-1划分为j-1个奇数之和的划分数”,即f[i-1][j-1];对于不包含1的划分方案,可以使用截边法对j个划分每一个都去掉一个1,转化为“将i-j划分为j个偶数之和的划分数”,即g[i-j][j]。

所以f[i][j]=f[i-1][j-1]+g[i-j][j]

f
[0]+f
[1]+……+f

为将n划分为若干奇数的划分数,为问题4的答案。

继续上代码, 有注释

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 55

using namespace std;

int num[maxn][maxn];//i划分为k个正整数之和
int num2[maxn][maxn];//划分成若干个不同的正整数之和
int f[maxn][maxn];//划分成若干个奇正整数之和
int g[maxn][maxn];

void init()
{
int i, j;
for (int i = 1; i < maxn; i++)
num[i][0] = num[0][i] = num2[i][0] = num2[0][i] = 0;
for (int i = 1; i < maxn; i++)
{
for(int j = 1; j < maxn; j++)
{
if (i < j)
num[i][j] = 0;
else if (i == j)
num[i][j] = 1;
else//           最小划分结果是1       不含一
num[i][j] = num[i - 1][j - 1] + num[i - j][j];
}
}
f[0][0] = 1, g[0][0] = 1;
for (i = 1; i < maxn; i++)
{
for (j = 1; j <= i; j++)
{
g[i][j] = f[i - j][j];
f[i][j] = f[i - 1][j - 1] + g[i - j][j];
}
}
}

long long fun2(int n, int m)//计算将n进行划分, 最大的划分结果不大于m
{
if (n == 1 || m == 1)
return 1;
else if (n < m)
return fun2(n, n);
else if (n == m)
return (1 + fun2(n, m - 1));
else
return (fun2(n, m - 1) + fun2(n - m, m));
}

int fun(int n, int m)//计算将n进行划分, 最大的划分结果小于m
{
if (n == 1 && m == 1)
return 1;
else if (m == 1)
return 0;
else if (n < m)
return fun(n, n);
else if (n == m)
return (1 + fun(n, m - 1));
else
return (fun(n, m - 1) + fun(n - m, m - 1));
}

int main(void)
{
init();
int n, m, res;
while (scanf("%d %d", &n, &m) != EOF)
{
printf("%d\n", num
[m]);
printf("%d\n", fun(n, n));
printf("%lld\n", fun2(n, n));
res = 0;
for (int i = 1; i <= n; i++)
res += f
[i];
printf("%d\n", res);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: