如果我必须删除子数组中的最大元素,如何找到子数组的最大和

Posted

技术标签:

【中文标题】如果我必须删除子数组中的最大元素,如何找到子数组的最大和【英文标题】:How do I find the maximum sum of subarray if i have to delete the largest element in the subarray 【发布时间】:2021-07-30 22:03:23 【问题描述】:
def maxsub(a,N):
    max_so_far = a[0]
    curr_max = a[0]

    for i in range(1,N):
        curr_max = max(a[i], curr_max + a[i])
        max_so_far = max(max_so_far,curr_max)
    return max_so_far


N = int(input())
arr = [int(input()) for _ in range(N)]

if all(x > 0 for x in arr) == True:
    print(sum(arr) - max(arr))
else:
    print(maxsub(arr,N))

此代码有助于找到任何子数组的最大总和,但如果我必须删除其中的最大元素,我需要找到子数组的最大总和。

例如 如果我们在一个数组中有 7 个元素作为 [0,-11,5,5,-10,0,50] '如果我们必须删除其最大元素的子数组的最大和'将是5 对于 5 个元素 [-2,10,-2​​,10,6],答案将是 14 我要在这里做什么?

【问题讨论】:

看起来像一个竞争问题——你能链接到源吗?还有多快是可以接受的? 【参考方案1】:

另一种方法可能是:

 def maxsub(a,N):
    bestSumsWithoutMax=sys.float_info.min
    bestSum=0
    for i in range(len(a)-1):
        LastInd = min(len(a)+1,i+N+1)
        for j in range(i+2,LastInd):
            subA = a[i:j]
            subSum =sum(subA)
            subSumWM =subSum-max(subA)
            if(bestSumsWithoutMax<subSumWM):
                bestSumsWithoutMax=subSumWM
                bestSum = subSum
    return bestSumsWithoutMax, bestSum

  sumsWithoutMax, associatedSum=  maxsub(a,N)
  print("%f  %f" % (associatedSum, sumsWithoutMax))


请注意,如果您正在处理大型数组,该算法的性能可能与更明确的索引产生的性能不同。

上面的代码可以浓缩为:

 def maxsub2(a,N):
    bestSumWMAndIndex = max([(sum(a[i:j])- max(a[i:j]),i,j) for i in range(len(a)-1) for j in range(i+2,min(len(a)+1,i+N+1))])
    return bestSumWMAndIndex[0], sum(a[bestSumWMAndIndex[1]:bestSumWMAndIndex[2]])

 sumsWithoutMax, associatedSum=   maxsub2(a,N)

 print("%f  %f" % (associatedSum, sumsWithoutMax))

编辑-----------------------------------

如果性能是关键,首先考虑用另一种语言进行编程。如果一定要坚持 Python,可以试试:

  def maxsub3(a,N):
    bestSumsWithoutMax=sys.float_info.min
    bestSum=0    
    for i in range(len(a)-1):
        LastInd = min(len(a),i+N)
        subAini = a[i:i+2]
        subSum =sum(subAini)
        maxA = max(subAini)
        subSumWM =subSum-maxA
        if(bestSumsWithoutMax<subSumWM):
            bestSumsWithoutMax=subSumWM
            bestSum = subSum
        for j in range(i+2,LastInd):
            A = a[j]
            subSum+=A
            if(A>maxA):                
                subSumWM+=maxA
                maxA=A
            else:
                subSumWM+=A

            if(bestSumsWithoutMax<subSumWM):
                bestSumsWithoutMax=subSumWM
                bestSum = subSum

    return bestSumsWithoutMax, bestSum

sumsWithoutMax, bestSum=   maxsub(b,N)
print("%f  %f" % (bestSum, sumsWithoutMax))

【讨论】:

什么是 N,为什么需要它? N 是子数组的最大大小。我不知道为什么需要它,但在问题中使用了它,所以我只是保留了这个要求。 maxsub3([-10, 7, -4, 1, 5], 5) 似乎返回了(1, 8)。我认为正确的结果是(2, 9) 对不起,你是对的。它必须是for j in range(i+2,LastInd):。我正在纠正我的答案【参考方案2】: 修改 maxSub() 函数以返回最大子数组的开始和结束索引。 然后取该子数组的 max(),并从子数组的最大值中减去它

这里有一些代码。 max_finder() 返回最大总和、开始、结束索引。我按照Kadane's Algorithm 描述的here 实现了它

def max_finder(a):
    cur_max, cur_start, cur_end = a[0], 0, 0
    max_so_far, start_so_far, end_so_far = a[0], 0, 0
    for i in range(1, len(a)):
        if a[i] > cur_max+a[i]:
            cur_max, cur_start, cur_end = a[i], i, i
        else:
            cur_max, cur_end = cur_max + a[i], i
        if (cur_max - max(a[cur_start: cur_end+1])) > (max_so_far - max(a[start_so_far: end_so_far+1])):
            max_so_far, start_so_far, end_so_far = cur_max, cur_start, cur_end
    return max_so_far, start_so_far, end_so_far
然后
max_sum, start, end = max_finder(a)
max_val = max(a[start: end+1])
print(max_sum - max_val)

【讨论】:

这在 [5, -100, 1, 1] 这样的实例上失败,因为它被大 5 “引诱”,然后消失了。 是的,在我看来是正确的,我知道@j_random_hacker 在说什么。需要详细说明吗? 抱歉,请改用[1, 1, -100, 5]。 (您的 max_finder() 本身有一个错误:max_finder([5, -100, 1, 1]) 应该是 (5, 0, 0) 但它错误地返回 (2, 2, 3)。我给出的示例输入都有总和为 5 的子数组。) 我很抱歉@j_random_hacker,这在您的第一次输入本身上是不正确的,我没有注意到。我将相应地编辑该功能。谢谢。 没问题,但更大的问题是,既然max_finder() 正确找到了最大和间隔,both 我的示例输入在正确时给出最终答案 0答案是 1。【参考方案3】:

这是一个重复出现,对于随机数据来说似乎相当快,但对于大部分排序的数据来说速度较慢)。对于 3000 个元素,10-20 times faster 似乎比 Amo Robb 的 maxsub3 函数(对于随机的、未排序的数据)。 repl 还包括针对蛮力的准确性测试。重复是天真的 - 一些向后运行可能会根据max_subarray 阈值查找最佳解决方案。

f(i, is_max, subarray_max) 表示以ith 元素结尾的最大和, 其中is_max 表示元素是否为最大值,subarray_max 是 子数组的最大值。那么:

# max isn't used if the element 
# ending the subarray is fixed
# as the maximum.
def f(A, i, is_max, subarray_max, memo, ps, pfxs):
  key = str((i, is_max, subarray_max))

  if key in memo:
    return memo[key]

  if is_max:
    if i == 0 or A[i-1] > A[i]:
      return 0

    result = f(A, i - 1, False, A[i], memo, ps, pfxs)
    memo[key] = result

    return result
  
  # not is_max
  if i == 0:
    if A[i] > subarray_max:
      return 0
    return max(0, A[i])

  # If max is not defined,
  # we MUST include all previous
  # elements until the previous equal or
  # higher element. If there is no
  # previous equal or higher element,
  # return -Infinity because a subarray
  # ending at A[i] cannot correspond
  # with false is_max.
  if subarray_max == None:
    prev = ps[i]
    if prev == -1:
      return -float('inf')

    best = -float('inf')
    temp = ps[i]

    while ps[temp] != -1:
      candidate = pfxs[i] - pfxs[temp] + f(A, temp, True, None, memo, ps, pfxs)

      if candidate > best:
        best = candidate
        # The prev equal or higher could still
        # be smaller to another.
      candidate = pfxs[i] - pfxs[temp] + f(A, temp, False, None, memo, ps, pfxs)
      if candidate > best:
        best = candidate

      temp = ps[temp]

    candidate = pfxs[i] - pfxs[temp] + f(A, temp, True, None, memo, ps, pfxs)

    if candidate > best:
        best = candidate

    memo[key] = best

    return best
  
  # If max is defined, the previous
  # equal or higher could be higher
  # than max, in which case we need
  # not include all elements in between.
  if A[i] > subarray_max:
    return 0

  result = max(0, A[i] + f(A, i - 1, False, subarray_max, memo, ps, pfxs))

  memo[key] = result

  return result

def g(A):
  memo = 
  best = -float('inf')

  ps = find_prev_greater_elements(A)

  pfxs = [A[0]] + [None] * len(A)
  for i in range(1, len(A)):
    pfxs[i] = A[i] + pfxs[i-1]

  for i in range(len(A)):
    best = max(best, f(A, i, True, None, memo, ps, pfxs))
    if i > 0:
      best = max(best, f(A, i, False, None, memo, ps, pfxs))

  return best


# Adapted from https://***.com/a/9495815/2034787
def find_prev_greater_elements(xs):
  ys=[-1 for x in xs]
  stack=[]
  for i in range(len(xs)-1, -1, -1):
    while len(stack)>0 and xs[i] >= xs[stack[-1]]:
      ys[stack.pop()]=i
    stack.append(i)
  return ys

【讨论】:

以上是关于如果我必须删除子数组中的最大元素,如何找到子数组的最大和的主要内容,如果未能解决你的问题,请参考以下文章

力扣技巧之动态规划力扣300:最大递增子序列C++

最大连续子数组(元素数量最多)

如何找到具有最小k长度和最大和的子数组?

和等于 0 的最大子数组

算法 LC 动态规划 - 最大递增子序列

找到总和最大的递增子序列