最大化导致由值限定的排序数组之和的因子数量

时间:2012-11-13 13:01:56

标签: algorithm sorting

我有一个大小为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

3 个答案:

答案 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)中做到这一点:

CUMULATIVE SUM

预先计算存储和(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之间的任何值都会使总和最小化。

ITERATIVE SEARCH

您现在可以一次扫描元素以找到最佳值。

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 CODE

这是算法的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搜索减少。