如何从给定数组中找到子集的最大和。 (子集不能没有大于任何两个元素之和的元素)

Posted

技术标签:

【中文标题】如何从给定数组中找到子集的最大和。 (子集不能没有大于任何两个元素之和的元素)【英文标题】:How to find the largest sum of the subset from a giving array. (Subset can't not have element greater than sum of any 2 elements) 【发布时间】:2020-11-12 10:25:13 【问题描述】:

我在编码面试中遇到了一个有趣的挑战。我需要帮助找到另一种更好的方法来解决问题。

问题:给一个数组有 N 个元素。

我们需要返回满足以下条件的子集的最大和。

条件:子集中任意元素不能大于子集中任意2个元素之和。

子集可以有 0 到 N 个元素。如果子集有1到2个元素(不需要遵循条件

示例: N = [10,4,4,4,5]

结果将是 4+4+4+5 = 17。输出是 17

*注意:我们只需要返回 Sum(我们不需要返回子集)

第二个例子

N = [100,99,98,4,4,4,4,4,5]

结果将是 100+99+98。输出为 297

我的解决方案:

我找到每个可能的子集,然后找到满足条件的任何子集的总和。 然后我找到子集的最大总和

n = [100,4,4,4,5]
def findPossibleSum(n):
   n.sort()                                     #I sort the n so that it can be easier to check the condition later
   result_temp = 0
   result = max(n)                              #for situation that largest sum belong to 1 element subarray.
   for i in range(0,len(n)+1):
       for j in range(i+1,len(n)+1):            #find all subset
           if len(n[i:j]) == 2:                 #check subset that have 2 elements
               result_temp = sum(n[i:j])
           if len(n[i:j]) > 2:                  #subset that have more then 2 elements
               if n[j-1] < (n[i] + n[i+1]):     #Because I sort the n from beginning. Now that I can check the largest elements(which is the one on the right side) and the sum of smallest elements(2 on the left side).(to determine the condition)
                  result_temp = sum(n[i:j])
               else:
                  pass
           if result_temp > result:
               result = result_temp
     return(result)

我的问题:我的解决方案的时间复杂度是 > O(N^3)。有没有办法在 O(N) 或 O(N^2) 中解决这个问题。

【问题讨论】:

你的意思是子数组还是子集?您的措辞与您的代码相矛盾。 是的,它是一个子集。我编辑了这个问题。 【参考方案1】:

这是一个复杂度为 O(n log n) 的算法,它不可能比 O(n log n) 做得更好,因为你需要先对数组进行排序。排序完成后,您可以在 O(n) 中完成剩余的工作。

观察 1

考虑一个满足条件的子集S 至少包含 3 个元素。那么所有包含数组 Amin(S)max(S) 之间数字的子集也满足条件。因此没有必要考虑数组A的所有子集。将 A 按降序排序就足够了,并且只考虑排序后的 A子数组

观察 2

如果满足以索引i 开头的条件的最长子数组的长度为k,则为了找到以索引j &gt; i 开头且满足条件的子数组具有更大的和第一个子数组相比,只考虑长度 > k 的子数组就足够了,因为所有其他子数组的和都会更小。这意味着我们只需要考虑 O(n) 个候选子数组。

观察 3

如果满足以索引i开始的条件的最长子数组的长度为k且其和为x,则以索引i+1开始且长度为@987654328的子数组的和@ 是 x - A[i] + A[i + k + 1],所以下一个候选子数组的总和总是可以在恒定时间内计算出来的。

算法(C#)

        int findMaxSum(int[] array)
        
            if (array.Length == 0) return 0;

            Array.Sort(array);
            Array.Reverse(array);

            int k = 0;
            int maxSum = 0;
            int currentSum = array[0];

            for (int i = 0; i < array.Length; i++)
            
                // find the longest subarray starting from i
                while (i + k + 1 < array.Length)
                
                    if (k > 0 && array[i] > array[i + k] + array[i + k + 1])
                    
                        // condition not satisfied, so we've found the longest subarray
                        k = k - 1;
                        break;
                    
                    // grow the subarray and increase the current sum
                    k = k + 1;
                    currentSum = currentSum + array[i + k];
                
                // check if the sum of the longest subarray starting from i is larger than the largest sum found so far
                maxSum = Math.Max(maxSum, currentSum);
                // subtract the first element to consider subarrays starting from i + 1
                currentSum = currentSum - array[i];
            

            return maxSum;
        
 

复杂性

对数组进行排序是 O(n log n)。之后,我们正在增长一个滑动子数组,它的长度增加直到不再满足条件,并且从左到右滑动直到到达数组的末尾。因此,尽管有嵌套循环,但这需要 O(n)。

所以总体复杂度是 O(n + n log n) = O(n log n)。

【讨论】:

以上是关于如何从给定数组中找到子集的最大和。 (子集不能没有大于任何两个元素之和的元素)的主要内容,如果未能解决你的问题,请参考以下文章

给定一个长度为 n 的数组,找到子集的 XOR 等于给定数字的子集数

给定一个长度为 n 的数组,找到子集的 XOR 等于给定数字的子集数

给定给定列的值,选择excel表的子集

从非前缀邻居的子集中找到最大收益值?

总和小于 M 的大小为 K 的子集的最大总和

改进子集解决方案