LC 2902. 和带限制的子多重集合的数目
2902. 和带限制的子多重集合的数目 - 力扣(LeetCode)
看了题解才知道这个是多重背包问题。之前也不知道这种问题有什么好的解法,题解 总结挺好的,两类方法:滚动数组和前缀和。两种方法其实都不是特别难理解,公式也还好,我把代码直接贴在下面。
其实这题我最没有想通的就是时间复杂度,如果还是按照DP的话,那么就是 `O(NM)` 了,其中N是nums长度,M是和上限。但是其实两者之间是存在某些关系的:因为对于最坏情况,nums里面每个值是不同的话,O(N^2) = O(M). 所以这里其实更像是 O(M^(3/2)) 这个时间复杂度上。如果按照题目提示中说的, `M < 2*10^4` 的话,那么这个时间复杂度是可以接受的。
class Solution: def countSubMultisets(self, nums: List[int], L: int, R: int) -> int: from collections import Counter cnt = Counter(nums) f = [cnt[0] + 1] + [0] * R del cnt[0] upper = 0 MOD = 10 ** 9 + 7 for x, c in cnt.items(): new_f = f.copy() upper = min(upper + x * c, R) for j in range(x, upper + 1): new_f[j] += new_f[j - x] if j >= (c + 1) * x: new_f[j] -= f[j - (c + 1) * x] new_f[j] %= MOD f = new_f return sum(f[L:]) % MOD class Solution: def countSubMultisets(self, nums: List[int], L: int, R: int) -> int: from collections import Counter cnt = Counter(nums) f = [cnt[0] + 1] + [0] * R del cnt[0] upper = 0 MOD = 10 ** 9 + 7 for x, c in cnt.items(): upper = min(upper + x * c, R) for j in range(x, upper + 1): f[j] = (f[j] + f[j - x]) % MOD for j in reversed(range((c + 1) * x, upper + 1)): f[j] = (f[j] - f[j - (c + 1) * x]) % MOD return sum(f[L:]) % MOD