如何从给定数组中找到子集的最大和。 (子集不能没有大于任何两个元素之和的元素)
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 个元素。那么所有包含数组 A 中 min(S) 和 max(S) 之间数字的子集也满足条件。因此没有必要考虑数组A的所有子集。将 A 按降序排序就足够了,并且只考虑排序后的 A 的 子数组。
观察 2
如果满足以索引i
开头的条件的最长子数组的长度为k
,则为了找到以索引j > 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 等于给定数字的子集数