6
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))

This code helps in finding maximum sum of any subarray, but I need to find what maximum sum of subarray >will be if I have to delete the largest element in it.

For e.g.
If we have 7 elements in an array as [0,-11,5,5,-10,0,50] the 'maximum sum of subarray if we have to delete its largest element' will be 5
For 5 elements [-2,10,-2,10,6] the answer will be 14
What will I have to do here ?

keancodeup
  • 69
  • 1

3 Answers3

1

Another approach could be:

 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))


Beware that the performance of this algorithm could be different from the one resulting from a more explicit indexing, in case you are dealing with large arrays.

The code above can be condensed to:

 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))

EDIT -----------------------------------

if performance is key, first consider programming it in another language. If you have to stick to Python, you can try:

  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))

Amo Robb
  • 810
  • 5
  • 11
0
  • modify your maxSub() function to return the start and end indices of your max subarray.
  • then take the max() of that subarray, and subtract it from the subarray's maximum

Here's some code. max_finder() returns the max sum, start, end indices. I implemented it following Kadane's Algorithm described 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
  • and then
max_sum, start, end = max_finder(a)
max_val = max(a[start: end+1])
print(max_sum - max_val)
ranvit
  • 99
  • 6
  • 1
    This fails on instances like `[5, -100, 1, 1]`, because it gets "lured in" by the big 5, which then goes away. – j_random_hacker May 09 '21 at 13:48
  • Yeah looks correct to me, idk what @j_random_hacker is talking about. care to elaborate? – ranvit May 09 '21 at 17:49
  • Sorry, try `[1, 1, -100, 5]` instead. (Your `max_finder()` itself has a bug: `max_finder([5, -100, 1, 1])` should be `(5, 0, 0)` but it incorrectly returns `(2, 2, 3)`. The example inputs I've given both have subarrays of sum 5.) – j_random_hacker May 09 '21 at 23:58
  • my apologies @j_random_hacker, this was incorrect on your first input itself and I didn't notice. I will edit the function accordingly. Thanks. – ranvit May 10 '21 at 05:42
  • No problem, but the bigger issue is that, now that `max_finder()` correctly finds the max-sum interval, *both* my example inputs give the final answer 0 when the correct answer is 1. – j_random_hacker May 10 '21 at 06:07
  • Lets take the example of [5, -100, 1, 1]. The max interval is [0:1] i.e only the first index. then, when we subtract the max value within this interval from the max-sum, it makes sense why my answer is 0. Can you explain why the correct answer would be 1? ty – ranvit May 10 '21 at 08:39
  • The question is asking for the maximum value that can be obtained by taking all elements in an interval, deleting the largest and summing the rest. If you take the interval `[1, 1]` and delete the largest element (one of the `1`s), and sum the rest, you get 1. – j_random_hacker May 10 '21 at 08:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/232160/discussion-between-ranvit-and-j-random-hacker). – ranvit May 10 '21 at 08:46
0

Here is a recurrence that seems to be pretty fast for random data but slower with largely sorted data). With 3000 elements, it seems about 10-20 times faster than Amo Robb's maxsub3 function (for random, not sorted data). The repl includes accuracy tests against brute force as well. The recurrence is naive - some of the backwards runs could have the best solution looked up based on the max_subarray threshold.

Let f(i, is_max, subarray_max) represent the largest sum ending at the ith element, where is_max indicates if the element is the maximum, and subarray_max is the maximum of the subarray. Then:

# 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://stackoverflow.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
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61