leetcode
leetcode 2451 ~ 2500
和带限制的子多重集合的数目

和带限制的子多重集合的数目

难度:

标签:

题目描述

You are given a 0-indexed array nums of non-negative integers, and two integers l and r.

Return the count of sub-multisets within nums where the sum of elements in each subset falls within the inclusive range of [l, r].

Since the answer may be large, return it modulo 109 + 7.

A sub-multiset is an unordered collection of elements of the array in which a given value x can occur 0, 1, ..., occ[x] times, where occ[x] is the number of occurrences of x in the array.

Note that:

  • Two sub-multisets are the same if sorting both sub-multisets results in identical multisets.
  • The sum of an empty multiset is 0.

 

Example 1:

Input: nums = [1,2,2,3], l = 6, r = 6
Output: 1
Explanation: The only subset of nums that has a sum of 6 is {1, 2, 3}.

Example 2:

Input: nums = [2,1,4,2,7], l = 1, r = 5
Output: 7
Explanation: The subsets of nums that have a sum within the range [1, 5] are {1}, {2}, {4}, {2, 2}, {1, 2}, {1, 4}, and {1, 2, 2}.

Example 3:

Input: nums = [1,2,1,3,5,2], l = 3, r = 5
Output: 9
Explanation: The subsets of nums that have a sum within the range [3, 5] are {3}, {5}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {1, 1, 2}, {1, 1, 3}, and {1, 2, 2}.

 

Constraints:

  • 1 <= nums.length <= 2 * 104
  • 0 <= nums[i] <= 2 * 104
  • Sum of nums does not exceed 2 * 104.
  • 0 <= l <= r <= 2 * 104

代码结果

运行时间: 176 ms, 内存: 17.4 MB


/*
思路:
使用Java Stream的组合功能可以简洁地实现同样的功能。我们将动态规划数组转换为Stream进行计算。
*/
import java.util.stream.IntStream;

public class Solution {
    public int countSubsets(int[] nums, int l, int r) {
        int MOD = 1000000007;
        int maxSum = 20000; // 最大可能的和
        int[] dp = new int[maxSum + 1];
        dp[0] = 1; // 初始空集
        for (int num : nums) {
            for (int j = maxSum; j >= num; j--) {
                dp[j] = (dp[j] + dp[j - num]) % MOD;
            }
        }
        return IntStream.rangeClosed(l, r).map(i -> dp[i]).reduce(0, (a, b) -> (a + b) % MOD);
    }
}

解释

方法:

此题解使用动态规划的方法解决问题。首先,它计算输入数组的总和 s,如果 s 小于 l,则直接返回 0。接着,它使用 Counter 来统计数组中每个元素的出现次数。动态规划数组 a 用于存储从和为 0 到 r 的子集的计数。a 的初始状态是 a[0] 等于 0 的元素的数量加 1。对于每个元素和其计数,更新动态规划数组 a,考虑包含不同数量的当前元素时的子集和。最终,根据 l 和 r 的值,计算和在 [l, r] 范围内的子集的数量并返回。

时间复杂度:

O(n * s)

空间复杂度:

O(r)

代码细节讲解

🦆
在这个解法中,为什么在计算数组总和s之后,选择直接返回0如果s小于l?
在题解中,如果数组的总和s小于下界l,意味着无论如何选择数组中的元素,其组合的和都不可能达到l。因此,在总和s小于l的情况下,不存在任何子多重集的和能在区间[l, r]内,直接返回0是一种效率优化,避免了不必要的计算。
🦆
为什么要调整r为不超过s的值,这样的调整对算法的正确性和效率有什么影响?
调整r为不超过s的值是为了确保考虑的子多重集的和不超过数组总和s的可能性。这样的调整避免了在动态规划中无意义的计算和内存使用,因为和大于s的子多重集本来就不存在。因此,这样的调整对算法的正确性无影响,但显著提高了算法的效率,特别是在r远大于s时。
🦆
动态规划数组a的初始状态设置为`a[0] = c[0] + 1`,这里的`c[0] + 1`代表什么意义?
在动态规划数组a中,`a[0] = c[0] + 1`表示包含0元素的子多重集的计数。`c[0]`是0元素在数组中的计数,即0元素出现的次数。`a[0] = c[0] + 1`意味着包括从选择0个0元素(即不包括任何0)到选择所有0元素的所有可能子多重集。因此,`a[0]`的初始值为选择0元素的所有可能方式的数量。
🦆
解法中提到删除0元素的计数(`del c[0]`),这对于整体算法的影响是什么?
在算法中删除0元素的计数是为了简化接下来的动态规划处理。一旦初始化了包含0元素的情况(`a[0] = c[0] + 1`),后续的动态规划就不需要再考虑0元素的影响。这样做可以减少后续计算的复杂性,并防止0元素的重复处理。实际上,这是一种优化措施,使得算法更专注于处理非零元素的计数和组合。

相关问题