我有一个大小为n的整数排序数组。这些值不是唯一的。我需要做的是
:给定一个B,我需要找到一个i<A[n]
,使得|A[j:1 to n]-i|
的总和小于B,并且该特定的总和贡献A [j]的最大数量。我有一些想法,但我似乎无法从天真的n * B和n * n算法中找到更好的东西。关于O(nlogn)或O(n)的任何想法?
例如:想象
A [n] = 1 2 10 10 12 14和B <7然后最好的i是12因为我实现了4 A [j] s对我的总和有贡献。 10和11也同样好我的原因如果i = 10我得到10 - 10 + 10 - 10 + 12-10 + 14-10 = 6 <7
答案 0 :(得分:1)
O(n)中的解:从最后开始并计算[n] -a [n-1]: 设d = 14-12 =&gt; d = 2且r = B-d =&gt; R = 5, 然后重复操作,但将d乘以2: d = 12-10 =&gt; d = 2且r = r-2 * d =&gt; R = 1, r = 1算法结束,因为总和必须小于B:
,数组索引为0..n-1
i=1
r=B
while(r>0 && n-i>1) {
d=a[n-i]-a[n-i-1];
r-=i*d;
i++;
}
return a[n-i+1];
也许一张图解释得更好
14 x
13 x -> 2
12 xx
11 xx -> 2*2
10 xxxx -> 3*0
9 xxxx
8 xxxx
7 xxxx
6 xxxx
5 xxxx
4 xxxxx
3 xxxxx
2 xxxxxx
1 xxxxxxx
答案 1 :(得分:0)
我认为你可以使用这三个技巧在O(n)中做到这一点:
预先计算存储和(A [0:k])的数组C [k] 这可以在时间O(n)中通过C [k] = C [k-1] + A [k]递归地完成。 这个数组的好处是你可以通过C [b] -C [a-1]计算和(A [a:b])。
由于您的元素已排序,因此很容易计算最佳i以最小化绝对值之和。事实上,最好的我将始终由中间条目给出。 如果列表的长度是偶数,则两个中心元素之间的所有i值将始终给出最小绝对值。
e.g。对于你的清单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
跟踪所看到的e-s的最大值,其得分< B,这是你的答案。
此循环最多可以循环2n次,因此它是O(n)。
A [s:e]的分数由sum | 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
答案 2 :(得分:0)
以O(N) with N size of array
方法尝试这种方式:
minpos = position of closest value to B in array (binary search, O(log(N))
min = array[minpos]
if (min >= B) EXIT, no solution
// now, we just add the smallest elements from the left or the right
// until we are greater than B
leftindex = minpos - 1
rightindex = minpos + 1
while we have a valid leftindex or valid rightindex:
add = min(abs(array[leftindex (if valid)]-B), abs(array[rightindex (if valid)]-B))
if (min + add >= B)
break
min += add
decrease leftindex or increase rightindex according to the usage
min is now our sum, rightindex the requested i (leftindex the start)
(可能有些指数不正确,这只是想法,而不是实施)
我猜,小b的平均情况是O(log(N))
。只有在我们可以使用整个数组时才会出现线性情况。
我不确定,但也许这可以在O(log(N)*k) with N size of array and k < N
中完成。我们必须以巧妙的方式使用bin搜索来在每次迭代中找到leftindex和rightindex,以便在每次迭代中可能的结果范围变小。这可以轻松完成,但我们必须处理重复,因为它们可能会破坏我们的bin搜索减少。