NYOJ 176 、POJ 1664 —— m划分为n个正整数的划分个数

整数划分(二)

时间限制: 1000 ms  |  内存限制:65535 KB 
描述

把一个正整数m分成n个正整数的和,有多少种分法?

例:把5分成3个正正数的和,有两种分法:

1 1 3

1 2 2

输入
第一行是一个整数T表示共有T组测试数据(T<=50)
每组测试数据都是两个正整数m,n,其中(1<=n<=m<=100),分别表示要拆分的正数和拆分的正整数的个数。
输出
输出拆分的方法的数目。
样例输入
2
5 2
5 3
样例输出
2
2

  n的m划分记为dp[n][m],可以分为两种类型:

    1.含有1的: dp[n][m][1]  

    2.不含有1的: dp[n][m][0]

  含有1的划分,可以看做{a1=1,a2,a3,...,am} (a1+a2+a3+...+am=n),那么是不是只要n减去1,就变成了n-1的m-1划分:{a2,a3,...,am}。即,dp[n][m][1]=dp[n-1][m-1]。含有1的n的m划分一一对应于n-1的m-1划分

  不含有1的划分,可以看做{a1,a2,a3,...,am} (a1+a2+a3+...+am=n && min(ai)>1),我们会发现如果全部ai均减去1,就变成了n-m的m划分:{a1-1,a2-1,a3-1,...,am-1}。即,dp[n][m][0]=dp[n-m][m]。不含有1的n的m划分一一对应于n-m的m划分

根据“加法原理”

        dp[n][m] = dp[n-1][m-1] + dp[n-m][m]  (1<m<n)

        dp[n][m] = 1  (m=1 或 n)

 

   这里,你会不会觉得我把两个情况简单相加有点草率?会不会因为dp[n][m]只有①和②其中一种情况,而我却考虑了两种而导致算错?

答案是不会!

  1)假设n的m划分都含有1,这时按道理说dp[n][m] = dp[n-1][m-1]就好了,而我们加多了一个dp[n-m][m]。但是,我们会发现如果n的m划分都含有1,那么必定有:n-m<m,即dp[n-m][m]是为0的,故不会有问题!

      2)假设n的m划分都不含有1,除非n=m,否则不可能有这样的n和m(我们的条件是:n>m)

故,当n>m时,dp[n-1][m-1] 一定要有,dp[n-m][m]是用来修正的,对于1)的情况不会有影响,但是对于不满足1)的情况有正面作用

 自底向上实现的版本:

#include <cstdio>
#include <iostream>

using namespace std;

const int MAXN = 105;

int dp[MAXN][MAXN];

void init()
{
    for(int i=1; i<MAXN; i++) {
        for(int j=1; j<MAXN; j++) {
            if(j>i)    dp[i][j] = 0;
            else if(j == i)    dp[i][j] = 1;
            else    dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
        }
    }
}

int main ()
{
    init();
    
    int T, m, n;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &m, &n);
        printf("%d\n", dp[m][n]);
    }
    return 0;    
} 
View Code

 

POJ题目:放苹果

这里与上面的不同就是,poj允许有空的盘子,而NYOJ不允许有。其实,就是变成了将数字m划分为1,2, ... , n个正整数的划分个数之和

#include <cstdio>
#include <iostream>

using namespace std;

int dp[15][15];

int main ()
{
    int T, m, n;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &m, &n);
        for(int i=1; i<=m; i++) {
            dp[i][1] = 1;
            for(int j=2; j<=n; j++) {
                if(j>i) {
                    dp[i][j] = 0;
                }
                else if(j == i) {
                    dp[i][j] = 1;
                }
                else    dp[i][j] = dp[i-1][j-1] + dp[i-j][j];
            }
        }
        int ans=0;
        for(int i=1; i<=n; i++)    ans += dp[m][i];
        printf("%d\n", ans);
    }
        
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/AcIsFun/p/5344886.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值