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

Posted sun897949163

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解:整数划分问题(DP)相关的知识,希望对你有一定的参考价值。

总时间限制: 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划分为若干正整数之和的划分数

  1. 若划分的多个整数可以相同
      设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[n][n]可以解决问题1,dp[n][k]表示将n划分为最大数不超过k的划分数,可以解决问题3。

  2. 若划分的正整数必须不同
      设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][n]表示将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[n][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[n][0]+f[n][1]+……+f[n][n]为将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[n][m]);
        printf("%d\\n", fun(n, n));  
        printf("%lld\\n", fun2(n, n));
        res = 0;
        for (int i = 1; i <= n; i++)
            res += f[n][i];
        printf("%d\\n", res);
    
    return 0;

以上是关于题解:整数划分问题(DP)的主要内容,如果未能解决你的问题,请参考以下文章

整数划分dp

DP专题:划分数问题

hdu 5230 整数划分 dp

整数划分

划分数系列问题

整数划分类型题目