最小切片位置 - N 阶算法

问题描述 投票:0回答:4

给出一个由 N 个整数组成的非空零索引数组 A。一对整数 (P, Q),使得 0 ≤ P < Q < N, is called a slice of array A (notice that the slice contains at least two elements). The average of a slice (P, Q) is the sum of A[P] + A[P + 1] + ... + A[Q] divided by the length of the slice. To be precise, the average equals (A[P] + A[P + 1] + ... + A[Q]) / (Q − P + 1).

编写一个函数:

int 解(int A[], int N);

给定一个由 N 个整数组成的非空零索引数组 A,返回具有最小平均值的切片的起始位置。 如果有多个具有最小平均值的切片,则应返回该切片的最小起始位置。

假设:

N是[2..100,000]范围内的整数; 数组 A 的每个元素都是 [−10,000..10,000] 范围内的整数。 复杂性:

预计最坏情况时间复杂度为O(N); 预期最坏情况的空间复杂度为 O(N),超出输入存储(不计算输入参数所需的存储)。

你可以只发布顺序为 N 的解决方案吗?

algorithm
4个回答
1
投票

如果

A
只有正数,你可以这样做:

pos = 0
min_avg = A[0] + A[1]
for (i=2; i<N; i++)
    m = A[i-1] + A[i]
    if (m < min_avg)
        min_avg = m
        pos = i-1
return pos

这只是取两个数字的切片的平均值,因为较大切片的平均值不能小于较小切片的最小值。

如果A有负数,可以先将所有值向上调整:

offset = min(A)
for (i=0; i<N; i++)
    A[i] -= offset

结合之前的算法:

offset = min(A) * 2              (because we're adding two numbers below)
pos = 0
min_avg = A[0] + A[1] - offset
for (i=2; i<N; i++)
    m = A[i-1] + A[i] - offset
    if (m < min_avg)
        min_avg = m
        pos = i-1
return pos

0
投票

首先,我们应该知道一个数学模式: 当列表的长度大于3时,可以将其分为长度为2和3的若干子分片。例如列表的长度为5,则可以由2和3组成;如果长度为4,则可以由2和2组成。

这样,如果我们有一个长度为 N 的列表:[a, b, c, d, ...],并且 N > 3,我们将列表中元素的总和设置为 Sum,并且平均值为 Avg = Sum/N。

我们要证明,在这个列表中,至少有一个长度为2或3的子切片的平均值小于或等于Avg。

我们使用反证法,假设所有子切片的平均值都大于 Avg。

将列表分为几个连续的子片,每个子片的长度为 2 或 3,其中最后一个子片的长度可以为 2 或 3:

[a1,a2], [a3,a4], ......, [aN-2, aN-1, aN] 或者 [a1,a2], [a3,a4], ......, [aN-1, aN]

根据我们的假设,每个子切片的平均值都大于Avg;

(a1 + a2)/2 > 平均值; (a3 + a4)/2 > 平均值; ......

将所有不等式相加,我们得到:

(a1 + a2)/2 + (a3 + a4)/2 + ... + (aN-1 + aN)/2 > 平均值 * (N/2)

不等式左边等于 Sum/2 或大于 Sum/2(如果最后一个子切片的长度为 3);

Sum/2 > Avg * (N/2) => Sum > Avg * N => Sum > (Sum/N) * N => Sum > Sum,这是一个矛盾。

我们得到了矛盾,所以我们的假设不成立。

结论是,如果有一个长度大于3的列表,那么它一定有一个长度为2或3的子切片,其平均值小于或等于整个列表的平均值。

这样我们只需求所有长度为2或3的子切片的最小平均值即可;

这是一个Python版本示例

def solution(A):
    n = len(A)
    if n == 2:
        return 0
    min_avg = float('inf')
    min_start = 0
    for i in range(n - 1):
        avg = (A[i] + A[i + 1]) / 2
        if avg < min_avg:
            min_avg = avg
            min_start = i
    for i in range(n - 2):
        avg = (A[i] + A[i + 1] + A[i + 2]) / 3
        if avg < min_avg:
            min_avg = avg
            min_start = i
    return min_start

-1
投票

我认为你是对的,我能做的最好的就是 O(N2) 解决方案(这是在 Python 中):

from random import randint

N = 1000
A = [randint(-10000, 10000) for _ in xrange(N)]

def solution(A, N):
    min_avg = 10001
    for p in xrange(N):
        s = A[p]
        for q in xrange(1,N-p):
            s += A[p+q]
            a = s / (q+1.)
            if a < min_avg:
                min_avg = a
                pos = (p, q+1)
    return pos

print solution(A, N)

但是,较大切片的平均值倾向于原始范围的平均值(中间)。 在这种情况下,平均值为零,介于 -10000 和 10000 之间。大多数情况下,最小平均值是两个值的切片,但有时它可以是三个值的切片,很少可以是更多值。 所以我认为我之前的答案适用于大多数(> 90%)的情况。 这实际上取决于数据值。


-1
投票
    #include <assert.h>
    struct Slice { unsigned P, Q; };
    struct Slice MinSlice( int A[], unsigned N ) {
        assert( N>=2 );
        // find min slice of length 2
        unsigned P = 0;
        double min_sum = A[P] + A[P+1];
        for (unsigned i = 1; i < N-1; ++i)
            if ( min_sum > A[i] +A[i+1] ) {
                P = i;
                min_sum = A[P] + A[P+1];
            }
        unsigned Q = P+1;
        double min_avg = min_sum / 2;
        //extend the min slice if the avg can be reduced.
        //(in the direction that most reduces the avg)
        for (;;) {
            if ( P > 0 && ( Q >= N-1 || A[P-1] <= A[Q+1] ) ) {
                //reducing P might give the best reduction in avg
                double new_sum = A[P-1] + min_sum;
                double new_avg = new_sum / (Q - P + 2);
                if ( min_avg < new_avg )
                    break;
                min_sum = new_sum; 
                min_avg = new_avg; 
                --P;
             } else if ( Q < N-1 && ( P <= 0 || A[P-1] >= A[Q+1] ) ) {
                //increasing Q might give the best reduction in avg
                double new_sum = min_sum + A[Q+1];
                double new_avg = new_sum / (Q - P + 2);
                if ( min_avg < new_avg )
                    break;
                min_sum = new_sum; 
                min_avg = new_avg; 
                ++Q;
             } else
                 break;
        }
        struct Slice slice = { .P = P, .Q= Q };
        return slice;
    }
© www.soinside.com 2019 - 2024. All rights reserved.