题目描述

牛客原题: ✅ 补充题 143. 整数的 k 项无序划分计数

给定正整数 n 和 k,将 n 分成 k 个非空正整数部分,不同排列算同一种分法。

返回方案数对 1000000007 取模的结果。

示例 1:

输入: n = 7, k = 3
输出: 4
解释: 四种分法为 1+1+5、1+2+4、1+3+3、2+2+3;相同部分的不同排列不另计。

提示:

  • n、k 均为正整数。
  • 每份必须为正整数,不同排列视为同一种分法。
  • 答案对 1000000007 取模。

题意分析

不同排列只算一种,所以不能逐个位置选择数字来计数,否则同一组整数会因顺序不同被重复统计。按照是否包含 1 划分所有方案,能得到互斥且覆盖完整的两类子问题。

每类都通过一个可逆变换对应更小规模的无序划分:含 1 时删去一个 1,不含 1 时每份减去 1。由此推导递推,而不是套用有序组合数公式。

解法:按是否含 1 分类的整数划分 DP

核心思路

[!blue]

先定义二维含义:F(sum, parts) 表示把总和 sum 分成 parts 个无序正整数的方案数。按是否包含 1,把所有方案分成互不重叠的两类:

  • 含有 1:删去其中一个 1,剩下的方案为 F(sum-1, parts-1)。因为不区分顺序,多个 1 不会带来多个删除位置的计数。
  • 每份都至少为 2:给每一份减去 1,剩下的方案为 F(sum-parts, parts)。

所以两类相加即可。代码的 previous 保存上一种份数,current 保存当前份数;第二项来自当前行更小的总和,因此 sum 必须递增。边界 F(0,0)=1 表示空划分,parts > sum 时没有正整数划分。

例如把 5 分成 2 份,含 1 的只有 [1,4];不含 1 的 [2,3] 每项减一后对应把 3 分成 2 份的 [1,2],两类各一,共两种。

解题步骤

  1. k>n 时不可能分成 k 个正数,立即返回 0。
  2. 初始化 0 份构成总和 0 的方案数为 1。
  3. 按份数递增,当前总和递增,累加含 1 与全部至少 2 两类方案。
  4. 每次取模,最后返回 k 份组成 n 的状态。

代码实现

class Solution {
    public int partitions(int n, int k) {
        if (k > n) {
            return 0;
        }

        int mod = 1_000_000_007;
        int[] previous = new int[n + 1];

        previous[0] = 1;

        for (int parts = 1; parts <= k; parts++) {
            int[] current = new int[n + 1];

            for (int sum = parts; sum <= n; sum++) {
                current[sum] = (previous[sum - 1] + current[sum - parts]) % mod;
            }

            previous = current;
        }

        return previous[n];
    }
}
func partitions(n, k int) int {
    if k > n {
        return 0
    }
    const mod = 1_000_000_007
    previous := make([]int, n+1)
    previous[0] = 1
    for parts := 1; parts <= k; parts++ {
        current := make([]int, n+1)
        for sum := parts; sum <= n; sum++ {
            current[sum] = (previous[sum-1] + current[sum-parts]) % mod
        }
        previous = current
    }
    return previous[n]
}

复杂度分析

  • 时间复杂度:$O(nk)$。
  • 空间复杂度:额外空间 $O(n)$。

关键点总结

[!green]

含 1 的方案删除一个 1;不含 1 的方案给每份减一。两类互斥且完整,不需要枚举不同排列。

易错点总结

[!yellow]

不是有序组合数;0份只组成总和0;当前层必须按sum递增。

相似题目

题目 难度 关联与区别
518. 零钱兑换 II 中等 同样不区分排列顺序,本题还固定份数,需要额外保存份数状态。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/39042305
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!