题目描述
将整数n分成k份,且每份不能为空,任意两个方案不能相同(不考虑顺序)。
例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;
1,5,1;
5,1,1;
问有多少种不同的分法。
输入:n,k ( 6 < n ≤ 200,2 ≤ k ≤ 6 )
输出:一个整数,即不同的分法。

方法一:递归求解

求解思路
对于本题目要求将整数n分成k份的个数,我们使用递归的思想进行解答。首先我们明确递归的终止条件为分成了k份。然后递归进行推进,当份数加1时,我们设置rest和use两个变量,其中rest减去已经分配的数字,use则是当前尝试分配的数字,根据上述描述,最终求出题目的答案即可。

图片说明

解题代码

import java.util.*;
public class Solution {
    int k;
    int count; // 存储分类数
    public int divideNumber (int n, int k) 
    {   //初始化k和cnt
        this.k=k;
        this.count=0;
        dfs(0,n,1); // 开始递归
        return count; // 返回结果
    }
    private void dfs(int depth,int rest,int use)
    {   //如果达到k份,递归终止
        if(depth==k)
        {
            if(rest==0)
            {
                count++;
            }
            return;
        }
        for(int i=use;i<=rest;i++)
        {
            dfs(depth+1,rest-i,i);
        }
    }
}

复杂度分析
时间复杂度:将数n分成k份,即看成n个1,在n-1个空格中放入k-1个挡板,并且不需要考虑其顺序。因此时间复杂度为(图片说明 )
空间复杂度:使用深度为k的递归栈,因此空间复杂度为

方法二:动态规划--优化求解

求解思路
对于本题目我们也可采用动态规划的思想来进行求解,其中dp[i][j]表示数字i分成j份总共有多少种分法。并且置初值dp[0][0] = 1.对于状态转移过程,我们分为两种情况进行讨论,一种是至少有一份分配了数字1,另一种是没有分配数字1.那么如果至少分配了一份是数字1,那么剩下的分发就是dp[i-1][j-1],即将数字i-1分为j-1.如果没有分配数字1,那么我们一定可以将j份都提前放一个数字1,剩下的就是dp[i-j][j],即将数字i-j分为j份,因此我们根据上述想法,写出转移方程为dp[i][j]=(dp[i-1][j-1]+dp[i-j][j])%mod

图片说明

解题代码

import java.util.*;
public class Solution {
    public int divideNumber (int n, int k) {
        int[][] mydp=new int[n+1][k+1]; // 初始化数组
        mydp[0][0]=1; // 赋初值
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=k;j++)
            {   //由于每份不能为空,所以划分数肯定大于总份数
                if(i>=j)
                {   //分为至少存在一份是1,和所有份数大于1两种情况
                    mydp[i][j]=(mydp[i-1][j-1]+mydp[i-j][j])%1000000007; // 状态转移方程
                }
            }
        }
        return mydp[n][k]; // 返回结果
    }
}

复杂度分析
时间复杂度:两层循环,因此时间复杂度为(图片说明 )
空间复杂度:需要引入额外的dp数组,因此空间复杂度为,其中k为份数。