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