我认为您可以使用以下三个技巧在 O(n) 中做到这一点:
累计金额
预先计算一个存储 sum(A[0:k]) 的数组 C[k]。
这可以通过 C[k]=C[k-1]+A[k] 在时间 O(n) 中递归地完成。这个数组的好处是你可以通过 C[b]-C[a-1] 计算 sum(A[a:b])。
最佳中点
因为您的元素已排序,所以很容易计算最佳 i 以最小化绝对值的总和。事实上,最好的 i 总是由中间条目给出。如果列表的长度是偶数,则两个中心元素之间的所有 i 值将始终给出最小绝对值。
例如,对于您的列表 10、10、12、14,中心元素是 10 和 12,因此 i 在 10 和 12 之间的任何值都会使总和最小化。
迭代搜索
您现在可以一次扫描元素以找到最佳值。
1. Init s=0,e=0
2. if the score for A[s:e] is less than B increase e by 1
3. else increase s by 1
4. if e<n return to step 2
跟踪所见分数 < B 的 es 的最大值,这就是你的答案。
这个循环最多可以循环 2n 次,所以它是 O(n)。
A[s:e] 的分数由总和 |A[s:e]-A[(s+e)/2]| 给出。
令 m=(s+e)/2。
score = sum |A[s:e]-A[(s+e)/2]|
= sum |A[s:e]-A[m]|
= sum (A[m]-A[s:m]) + sum (A[m+1:e]-A[m])
= (m-s+1)*A[m]-sum(A[s:m]) + sum(A[m+1:e])-(e-m)*A[m]
我们可以使用预先计算的数组 C[k] 来计算这个表达式中的总和。
编辑
如果端点必须始终为 n,那么您可以使用此替代算法:
1. Init s=0,e=n
2. while the score for A[s:e] is greater than B, increase s by 1
蟒蛇代码
这是该算法的python实现:
def fast(A,B):
C=[]
t=0
for a in A:
t+=a
C.append(t)
def fastsum(s,e):
if s==0:
return C[e]
else:
return C[e]-C[s-1]
def fastscore(s,e):
m=(s+e)//2
return (m-s+1)*A[m]-fastsum(s,m)+fastsum(m+1,e)-(e-m)*A[m]
s=0
e=0
best=-1
while e<len(A):
if fastscore(s,e)<B:
best=max(best,e-s+1)
e+=1
elif s==e:
e+=1
else:
s+=1
return best
print fast([1,2,10,10,12,14],7)
# this returns 4, as the 4 elements 10,10,12,14 can be chosen