[HOT 100] 0377. 组合总和 IV

1. 题目链接


377. 组合总和 Ⅳ - 力扣(LeetCode)


2. 题目描述


给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

3. 题目示例


示例 1 :

输入: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)
请注意,顺序不同的序列被视作不同的组合。

示例 2 :

输入:nums = [9], target = 3
输出:0

4. 解题思路


采用动态规划 + 记忆化搜索(自顶向下递归)。核心思想是:

  • 对于当前目标值 i,遍历 nums 中的每个数 x,若 x <= i,则组合数等于 i - x 的组合数之和。

状态转移方程:

dp[i] = sum(dp[i - x]),其中 x ∈ nums 且 x <= i

关键步骤

  1. 递归函数 dfs(i) 计算组成目标值 i 的组合数。
  2. 记忆化数组 memo 缓存已计算的子问题结果。
  3. 递归终止条件:当 i = 0 时,表示找到一种有效组合。

5. 题解代码


class Solution {
    
    
    public int combinationSum4(int[] nums, int target) {
    
    
        int[] memo = new int[target + 1];    // 记忆化数组,记录每个目标值的组合数
        Arrays.fill(memo, -1);               // 初始标记为未计算状态
        return dfs(target, nums, memo);      // 从目标值开始递归
    }

    private int dfs(int i, int[] nums, int[] memo) {
    
    
        if (i == 0) {
    
                            // 递归终止条件:目标值为0时,组合数为1
            return 1;
        }
        if (memo[i] != -1) {
    
                     // 已计算过,直接返回缓存结果
            return memo[i];
        }
        int res = 0;                         // 累计当前目标值的组合数
        for (int x : nums) {
    
                     // 遍历每个可能的数
            if (x <= i) {
    
                        // 只有当数不超过当前目标时才有效
                res += dfs(i - x, nums, memo); // 递归计算剩余部分的组合数
            }
        }
        return memo[i] = res;                // 结果存入缓存并返回
    }
}

6. 复杂度分析


时间复杂度

  • 每个目标值 i(从 0target)只会计算一次。
  • 每次计算需要遍历数组 nums(长度为 n)。
    总时间复杂度为 O(target × n),其中 n 是数组 nums 的长度。

空间复杂度

  • 递归调用栈深度为 O(target)(最坏情况下)。
  • 记忆化数组占用 O(target) 空间。
    综上,空间复杂度为 O(target)