给定a
个n
整数的数组,计算有多少个子序列(非连续的)sum % k = 0
:
1 <= k < 100
1 <= n <= 10^6
1 <= a[i] <= 1000
可以轻松实现O(n^2)
解决方案,但需要更快的O(n log n)
或O(n)
方式。
答案 0 :(得分:2)
这是subset sum问题。
一个简单的解决方案就是:
s = 0
dp[x] = how many subsequences we can build with sum x
dp[0] = 1, 0 elsewhere
for i = 1 to n:
s += a[i]
for j = s down to a[i]:
dp[j] = dp[j] + dp[j - a[i]]
然后,您只需返回所有dp[x]
的总和,即x % k == 0
。但这具有很高的复杂性:大约O(n*S)
,其中S
是所有元素的总和。 dp
数组也必须具有S
大小,您可能甚至无法为约束声明这些大小。
更好的解决方案是不首先迭代大于或等于k
的和。为此,我们将使用2个dp
数组:
dp1, dp2 = arrays of size k
dp1[0] = dp2[0] = 1, 0 elsewhere
for i = 1 to n:
mod_elem = a[i] % k
for j = 0 to k - 1:
dp2[j] = dp2[j] + dp1[(j - mod_elem + k) % k]
copy dp2 into dp1
return dp1[0]
其复杂性为O(n*k)
,并且是此问题的最佳选择。
答案 1 :(得分:0)
有一个O(n + k^2 lg n)
- 时间算法。计算输入数组mod c(0), c(1), ..., c(k-1)
的直方图k
(即,c(r)
mod r
有k
个元素。然后计算
k-1
product (1 + x^r)^c(r) mod (1 - x^k)
r=0
如下所示,其中简化多项式的常数项是答案。
我们不是用快速取幂方法评估每个因子然后相乘,而是将内容翻出来。如果所有c(r)
都为零,则答案为1
。否则,递归评估
k-1
P = product (1 + x^r)^(floor(c(r)/2)) mod (1 - x^k).
r=0
然后计算
k-1
Q = product (1 + x^r)^(c(r) - 2 floor(c(r)/2)) mod (1 - x^k),
r=0
通过利用因子的稀疏性来计算后者O(k^2)
。结果是P^2 Q mod (1 - x^k)
,通过天真卷积在时间O(k^2)
中计算。
答案 2 :(得分:0)
遍历a
并计算a[i] mod k
;应该k
这样的数量。
对k, 2*k, 3*k...etc.
的不同分区进行递归和记忆,其中部分小于或等于k
,并添加相应计数的产品。
例如,如果k
为10
,则部分分区为1+2+7
和1+2+3+4
;但是在记忆时,我们只需要计算一次数组k
中有多少对产生(1 + 2)
。
例如,k = 5, a = {1,4,2,3,5,6}
:
counts of a[i] mod k: {1,2,1,1,1}
products of distinct partitions of k:
5 => 1
4,1 => 2
3,2 => 1
products of distinct partitions of 2 * k with parts <= k:
5,4,1 => 2
5,3,2 => 1
4,1,3,2 => 2
products of distinct partitions of 3 * k with parts <= k:
5,4,1,3,2 => 2
answer = 11
{1,4} {4,6} {2,3} {5}
{1,4,2,3} {1,4,5} {4,6,2,3} {4,6,5} {2,3,5}
{1,4,2,3,5} {4,6,2,3,5}