找到一个子数组,其中每个对的总和大于给定的K.

时间:2012-10-22 15:39:56

标签: arrays algorithm

我得到了一个整数数组A.现在我必须找到一个子数组(原始数组的子序列),其中每对的总和大于或等于预定义的K.

我的想法: -

  • 将根据数组中的值范围对数组进行O(nlgn)或O(n)排序。
  • 找出排序数组中的sorted[i] + sorted[i+1]>=k
  • 将最高设置为sorted[i]
  • 遍历原始数组以删除小于max的所有值,即所需的子序列

对数组的所有元素重复上述步骤。

运行时间: - O(nlgn)

解决方案是否最佳?我们可以进一步改进吗?

示例: -

-10 -100 5 2 4 7 10 23 81 5 25

排序数组

-100 -10 2 4 5 5 7 10 23 25 81

K = 20

子阵列: -

10 23 25 81

如果问题是找出最长的子阵列,那么alestanis在答案中建议的算法会很好用:)

4 个答案:

答案 0 :(得分:3)

这是一个稍微不同的方法,由早期的一条评论暗示,类似于alestanis的答案,但略有不同,因为它不依赖于拆分数组。它只通过数组(尽管不能保证O(N)),只需要跟踪两个最小值以及正在考虑的子序列的起点和终点。

对于连续子序列,使所有可能的对总和为20,两个最小元素的总和必须> = 20.因此,首先考虑后续元素对(array[0]array[1]开始)。如果它们不总和为20或更多,则转到array[1]array[2]。如果它们总计达20或更多,则将右侧端点扩展一个。如果新元素大于其他两个元素,那么对于已经在子序列中的任何内容,它将总和为20或更大,并且您可以再次展开右手。如果它更少,那么你需要通过几个比较选择两个最少的元素,如果两个新的最小元素现在不总和为20或更多,那么从子序列中删除刚刚添加的元素,并注意这个特殊的子序列,然后从现有子序列的第二和第三个元素开始。最后,您通常会得到一个符合约束条件的子序列列表,并且应该很容易选择第一个或最大的或者您需要的任何内容。

示例,使用您列出的序列:

-10 -100 5 2 4 7 10 23 81 5 25

-10, -100开始。它们不总和为20,所以向右移动-100, 5。再次,这些不等于20,所以继续。总和为20的第一对是10, 23。现在,我们将范围扩展到10, 23, 81。 81大于两个最小值,因此我们再次扩展到10, 23, 81, 5。 5小于10和23,所以新的最小值是5和10,不等于20,所以加5是一个错误,我们需要回溯。我们发现10, 23, 81就是这样一个子序列。接下来我们继续23, 81,这将引导我们进入符合条件的子序列23, 81, 5, 25

因此,最后,我们有四个可能符合标准的子序列 - 10, 23, 8123, 81, 5, 2581, 5, 255, 25。一旦我们有一个包含原始列表中最后一个元素的解决方案,最后两个可以通过不找到其他解决方案来修剪,这将只留下前两个可能性。从那里我们可以选择第一个或最长的。

答案 1 :(得分:3)

这是一个相当简单的解决方案。

>>> def f(A, k):
...     solution = [item for item in A if 2*item >= k]
...     m = min(solution)
...     for item in A:
...         if item + m >= k and 2*item < k:
...             solution.append(item)
...             break
...     return solution
...
>>> f([-10, -100, 5, 2, 4, 7, 10, 23, 81, 5, 25], 20)
[10, 23, 81, 25]
>>>

答案 2 :(得分:2)

首先,您无法对您的设置进行排序。我认为问题的一部分是找到作为输入给出的原始数组的子数组。

这可以使用一些递归来解决:

  1. 找到阵列的两个最小值,m1m2
  2. 如果m1 + m2 < K,则将您的数组拆分为最多两个同时不包含m1m2的较小数组。如果m1m2的索引为ij的索引为i<j,则子阵列为[O, j-1][i+1, n]。 从步骤1开始重复。
  3. 如果m1 + m2 >= K那么您当前的数组是解决问题的可行方法:返回其长度。
  4. 添加一些修剪以丢弃无用数组
  5. 让我们在你的例子中应用这个:

    Initialize max = 0;
    A1 = -10* -100* 5 2 4 7 10 23 81 5 25
    

    它的两个最小值是-10和-100。围绕这些值拆分数组,这只给我们一个数组(我们很幸运!)

    A2 = 5 2* 4* 7 10 23 81 5 25
    

    A2的两个最小值是2和4.我们分成

    A3_1 = 5* 4*   and    A3_2 = 2* 7 10 23 81 5* 25
    

    这将继续以下迭代:

    A3_1 discarded    
    A3_2 becomes A4_1 = 2* 7* 10 23 81    A4_2 = 7* 10 23 81 5* 25
    
    A5_1 = 7* 10* 23 81
    A5_2 = 7* 10* 23 81        -> Duplicate, discarded
    A5_3 = 10* 23 81 5* 25
    
    A6_1 = 10* 23* 81          -> Yay! update max = 3
    A6_2 = 10* 23* 81          -> Length <= max. Discarded
    A6_3 = 23 81 5* 25         -> Yay! update max = 4
    

    在这个例子中,我通过以下方式修剪了搜索空间:

    • 消除重复的子集(这可以通过将它们存储在一个集合中来完成)
    • 丢弃短于或等于已知当前最大长度的子阵列

    此算法的复杂性为:

    • O(nlogn)平均值,
    • O(n ^ 2)最坏的情况。当数组被排序并且最小值始终位于数组的一侧时,会发生这种情况,因此数组不能拆分为更小的子数组(如示例的第一次迭代)。

答案 3 :(得分:0)

void sub_array(int ar[],int n,int val)
{
    int max=0;
    for(int i=0;i<n;i++)
    {
        if(ar[max]<ar[i])
            max=i;
    }
    int b[n];
    max=ar[max];
    int p=0;
    int min=0;
    for(int i=0;i<n;i++)
    {
        if(ar[i]+max>val)
        {
            b[p]=ar[i];
            if(ar[i]<max)
            {   
                min=p;
                max=ar[i];
            }
            p++;
        }
        else
        {
            if(ar[i]>max)
            {
                max=ar[i];
                b[min]=ar[i];
            }
        }
    }
    for(int i=0;i<p;i++)
    {
        cout<<b[i]<< "  " ;
    }
}