📜  总和等于 X 的子集计数 |第 2 组(1)

📅  最后修改于: 2023-12-03 15:25:40.283000             🧑  作者: Mango

总和等于 X 的子集计数

对于给定的数组和整数 X,找出数组中有多少个子集的和为 X。

问题描述

给定一个整数数组 nums 和一个目标值 X,找出数组中和等于 X 的子集的个数。

示例:

输入:nums = [1,2,3,4,5], X = 7 输出:2 解释:[3,4] 和 [2,5] 是和等于 7 的两个子集。

解法
  • 动态规划

使用动态规划,令 dp[i][j] 表示从数组的前 i 个元素中选取一些元素,使它们的和等于 j 的方案数。

当不选取 nums[i] 时,dp[i][j] = dp[i-1][j]。 当选取 nums[i] 时,dp[i][j] += dp[i-1][j-nums[i]]。 所以方程为:dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]。

原因:dp[i][j] 表示的是前 i 个元素中选取若干个元素和为 j 的方案数,那么 dp[i-1][j] 表示的就是不选取第 i 个元素,从前 i-1 个元素中选取若干个元素和为 j 的方案数。dp[i-1][j-nums[i]] 表示的就是选取了第 i 个元素,那么从前 i-1 个元素中选取若干个元素和为 j-nums[i] 的方案数。

最后 dp[n][X] 即为答案。

  • 递归

使用递归,从数组的最后一个元素开始向前搜索,每次递归分别传入当前元素的值和剩余的目标值。每次搜索到和为目标值的时候,计数器加 1。

代码实现
动态规划
def count_subsets(nums, X):
    n = len(nums)
    dp = [[0] * (X+1) for _ in range(n+1)]
    for i in range(n+1):
        dp[i][0] = 1
    for i in range(1, n+1):
        for j in range(1, X+1):
            if j >= nums[i-1]:
                dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]]
            else:
                dp[i][j] = dp[i-1][j]
    return dp[n][X]
递归
def count_subsets(nums, X):
    count = [0]  # 计数器
    def dfs(cur, target):
        if target == 0:  # 搜索到和为目标值
            count[0] += 1
            return
        if cur == -1:  # 没有任何元素了
            return
        if nums[cur] > target:  # 当前元素大于目标值,不能选
            dfs(cur-1, target)
            return
        dfs(cur-1, target)  # 不选当前元素
        dfs(cur-1, target-nums[cur])  # 选当前元素
    dfs(len(nums)-1, X)
    return count[0]
总结

此题可以使用动态规划或者递归来解决,动态规划比较直观而递归思路更为清晰。使用动态规划的时间复杂度是 O(nX),使用递归的时间复杂度是 O(2^n),因此使用动态规划更加高效。