Sakura晞月

导航

数的划分

题目描述

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。

例如:n=7,k=3,下面三种分法被认为是相同的。

1,1,5;

1,5,1;

5,1,1;

问有多少种不同的分法。


输入

每个测试文件只包含一组测试数据,每组输入两个整数n和k(6<n<=200,2<=k<=6)。


输出

对于每组输入数据,输出一个整数,即不同的分法。

下面是对样例数据的说明:

样例中的四种分法是:

1,1,5

1,2,4

1,3,3

2,2,3


样例输入

7 3

样例输出

4

提示

 

 

思路:

感觉这是一道非常基础的dp题目。

基础的动态转移方程是:dp[i][j] = dp[i-j][1]+ dp[i-j][2]+……+ dp[i-j][j]

首先,如果拿到一个整数 i ,因为题目中要求每份不能为空,因此必须先拿出 j 个数位将 j 份分别放上1,此时剩下 i - j个数。那么剩下的数如何处理呢?可以将其全部分到一份当中(dp[i-j][1]),也可以分到两份中(dp[i-j][2]),...,也可以分到 j 份中(dp[i-j][j]),而每一种分法都是不相同的,所以可以将其全部加起来,和即为dp[i][j]。

不过这个式子看起来并不简洁,为了求得一个简洁的式子,我们再求一个dp[i-1][j-1],

得到了这个简洁的结果

直接理解这个式子,其实就是现在要求的等于之前的等于两种可能的相加结果

1、dp[i-j][j]就是已经全部将j分完了,那么这个加不加上都不会产生新的可能。

2、dp[i-1][j-1]是还差一份需要分,正好1份对应这个1。

 

#include<iostream>
using namespace std;
int main()
{
    int n,k;
    cin >> n >> k;
    //全部初始化为0
    int dp[201][7] = {0};
    //对于dp[0][0]作特殊处理(为了后面的动态转移方程能够起作用)
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++)//i的取值范围是1~n(不能超过给定的整数)
    {
        for(int j = 1; j <= k; j++)//j的范围是1~k(不能超过需要划分的份数)
        {
            //划分的分数要小于等于该数本身
            if(j <= i)
                //动态转移方程
                dp[i][j] = dp[i-j][j] + dp[i-1][j-1];
        }
    }
    cout << dp[n][k] << endl;
    return 0;
}

 

posted on 2019-04-20 21:34  Sakura晞月  阅读(171)  评论(0编辑  收藏  举报