377. 组合总和 IV(Combination Sum IV)

频次 ★★★ · 难度 🟡 · 高频:字节

题目

不同整数数组 nums,找出总和为 target 的元素组合个数(顺序不同的序列视为不同组合)。

示例

输入: nums = [1,2,3], target = 4
输出: 7  ((1,1,1,1), (1,1,2), (1,2,1), (1,3), (2,1,1), (2,2), (3,1))

思路

完全背包求排列数dp[i] 表示凑成 i 的排列数。

dp[i] += dp[i - num] for each num ≤ i。

关键:外层遍历金额 i,内层遍历硬币 num。这与 518 题(组合数)内外层相反。因为排列考虑顺序,外层金额意味着”走到金额 i 时,最后一步可以用任意硬币”,这样不同顺序会被重复计数成不同方案。

代码

public int combinationSum4(int[] nums, int target) {
    int[] dp = new int[target + 1];
    dp[0] = 1;                               // 凑 0 有一种方式
    for (int i = 1; i <= target; i++) {       // 外层:金额(排列数)
        for (int num : nums) {                // 内层:硬币
            if (i >= num) {
                dp[i] += dp[i - num];
            }
        }
    }
    return dp[target];
}

复杂度

  • 时间:O(target × n),n = nums 长度
  • 空间:O(target)

边界条件

  • target = 0:返回 1
  • nums 中有超出 target 的数:跳过
  • 结果可能溢出:题目保证结果在 int 范围内,但中间量可能超过,实际提交时注意

变式

易错点

  • 内外层顺序是核心:外层金额 = 排列,外层硬币 = 组合。这道题和 518 的唯一区别
  • 题目虽然叫”组合总和 IV”,但实际求的是排列数(顺序不同算不同)
  • 注意 dp 数组可能越界,LeetCode 题目保证结果在 int 范围,但中间值可能溢出(C++ 需用 unsigned int,Java 可以)

面试追问

  • 为什么叫”组合”但求的是排列? LeetCode 中文翻译问题,英文题名 Combination Sum IV,但题目描述明确说”顺序不同的序列视为不同组合”
  • 和 518 的区别? 只有内外层循环顺序不同,面试中经常对比考察

关联题