给定一个非负整数列表,例如 0 , 1 , 2 ,. 。 。 , n 和常数正整数m。我们想要生成所有形式为' 0 ,' 1 ,' 2 的非负整数列表。 。 。 ,' n ,以便:
就时间而言,java的最佳算法是什么。我尝试过两种不同的东西。
令人惊讶的是,我的第二个想法明显快于第一个想法,但仍然相当缓慢。我该怎么做呢?这个问题在精神上与Generating the partitions of a number类似。
答案 0 :(得分:2)
这可以使用动态规划在多项式时间 O(nm 2 )中求解。
设 f(i,r)是构建序列a' 0 ,' 1 ,...的方法的数量。 。,' i 这样
(a 0 + a 1 ,... + a i - a' 0 - a ' 1 - ... - a' i )mod m = r 。
第一个参数的选择是显而易见的:我们将从左到右查看序列,并记录序列前缀的结果。 第二个参数是我们到目前为止模拟 m 的其余部分,因为为了构建所有可能的序列,我们必须考虑其间的所有可能的余数,而不仅仅是零。
要计算 f(i,r),我们需要考虑(a i - a' i )的可能选择mod m 并使用先前计算的 f(i - 1,*)。
如果(a i - a' i )mod m = 0,则有 u = [a i / m ] + 1个' i 的选择:这些是 i , a i - m ,a' i - 2 m ,...,a' i - u m 。 所以,我们必须在我们的答案中添加([a i / m ] + 1)* f(i - 1,r):每个可能的答案长度 i - 1 ,总余数 r ,我们可以选择[a i / m ] +' i 的1个可能值,使得新序列的总余数也 r 。
如果(a i - a' i )mod m = m - 1,有 v = [(一个 i - 1)/ m ] + 1个' i 的选择:这些是 i - 1, i - 1 - m ,a' i - 1 - 2 m ,...,'a i - 1 - v m 。 所以,我们必须添加([(a i - 1)/ m ] + 1)* f(i - 1,(r + m - 1) mod m)到我们的答案:对于长度 i - 1 的每个可能答案,总余数(r + m - 1) mod m ,我们可以选择每个[(a i - 1)/ m ] + 1个' i ,以便新序列的总余数现在为 r 。
...
如果(a i - a' i )mod m = 1,则 w = [(a i - ( m - 1))/ m ] + 1这样的' i < sub>:这些是 i - ( m - 1), i - ( m - 1) - m ,a' i - (m - 1) - 2 m ,...,a' i - ( m - 1) - w m 。 所以,我们必须添加([(a i - ( m - 1))/ m ] + 1)* f( i - 1,(r + 1) mod m)我们的答案:对于长度 i - 1 的每个可能答案,总余数( r + 1) mod m ,我们可以选择每个[(a i - ( m - 1))/ m ] +' i 的1个可能值,使得新序列的总余数现在为 r 。
总结一下,
f(i,r) = sum {by s = 0 to m - 1} of([(a 我 - s)/ m ] + 1)* f(i - 1,(r + m - s) mod m)
我们为所有 i 和所有 r 执行此操作。 基数将是 f(-1,0)= 1 (有一个空序列,总余数为0)和 f(-1,t)= 0 < em> t&gt; 0 (没有空序列,总余数> 0)。 答案是 f(n,0)(序列数a' 0 ,a' 1 ,...,a' i ,总余数为0)。
实现时,请注意舍入整数除法并采用非负模数:在接近CPU的语言中(如C或C ++ or even Java),整数除法和余数计算默认舍入为零,因此,当我们分别需要-1和1时,-1 / 2的整数结果将为0,-1 mod 2将为-1。