dp整数划分问题——03:复杂的整数划分问题

整数划分 :
  这道好题求:
  1. 将n划分成若干正整数之和的划分数。
  2. 将n划分成k个正整数之和的划分数。
  3. 将n划分成最大数不超过k的划分数。
  4. 将n划分成若干奇正整数之和的划分数。
  5. 将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>jdp[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]。


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>jdp[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]。


 

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

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

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

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

  (3) 若i>j,可以根据划分数中是否含有1分为两类:

                               若划分数中含有1,可以使用“截边法”将j个划分分别截去一个1

                                               把问题转化为i-jj-1个划分数,为dp[i-j][j-1]; 

                               若划分中不包含1,使用“截边法”将j个划分数的最下面一个数截去,

                                               将为题转化为求i-jj个划分数,为dp[i-j][j]。所以i>jdp[i][j]=dp[i-j][j-1]+dp[i-j][j]。



 

三 将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的答案。



另:

将整数划分成连续正整数之和:


如15可以划分成4种连续整数相加的形式:
15
7 8
4 5 6
1 2 3 4 5

    首先考虑一般的形式,设n为被划分的正整数,x为划分后最小的整数,如果n有一种划分,那么
结果就是x,如果有两种划分,就是x和x 、x + 1, 如果有i种划分,就是

x ;

x 、x + 1 ;

x、 x + 1、 x + 2 、... ;

x、 x + 1、 x + 2、 ... 、 x + i - 1;
将每一个结果相加得到一个公式(i * x + i * (i - 1) / 2) = n,i为当前划分后相加的正整数个数。
满足条件的划分就是使x为正整数的所有情况。
如上例,

当i = 1时,x = 15;

当i = 2时, x = 7;

当i = 3时,x = 4;

当i = 4时,x = 4/9;      (x不是正整数,因此,15不可能划分成4个正整数相加)

当i = 5时,x = 1;

当i = 6时,x < 1;          (所以i最大为5)


好了,咱们看题 大笑

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
提示
第一行: 4+1, 3+2,
第二行: 5,4+1,3+2
第三行: 5,1+1+3, 1+1+1+1+1+1




#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <map>

using namespace std;

int main()
{
    int n, k;
    while( cin >> n >> k )
    {
        int num[n+1][n+1];             // 表示把n分成k个数的dp数组;
        int num1[n+1][n+1];            // 表示把n分成不相等正整数的dp数组;
        int g[55][55], f[55][55];      // g数组为偶数dp数组, f数组为奇数dp数组;
        int i, j;
                                    // 初始化;
        for( i=0; i<=n; i++ )
        {
            for( j=0; j<=n; j++ )
            {
                num[i][j] = num1[i][j] = f[i][j] = g[i][j] = 0;
            }
        }
                                    // 这就是上面说的其中两种。
        for( i=1; i<=n; i++)
        {
            for( j=1; j<=n; j++)
            {
                if( i < j )
                {
                    num[i][j] = 0;
                    num1[i][j] = num1[i][j-1];
                }
                if( i == j )
                {
                    num[i][j] = 1;
                    num1[i][j] = 1 + num1[i][j-1];
                }
                if( i > j )
                {
                    num[i][j] = num[i-1][j-1] + num[i-j][j];
                    num1[i][j] = num1[i-j][j-1] + num1[i][j-1];
                }
            }
        }
                                 //  这里是最晕的,只要记住就好了,搞懂也没多大意义;
        f[0][0]=1; g[0][0]=1;
        for( i=1; i<=n; 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];

            }
        }

        cout << num[n][k] <<endl;
        cout << num1[n][n] <<endl;
        int sum = 0;
        for( i=0; i<=n; i++)
            sum += f[n][i];
        cout << sum <<endl;
    }
    return 0;
}





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值