2

my problem is that I'm given an array of with length l.

let's say this is my array: [1,5,4,2,9,3,6] let's call this A.

This array can have multiple sub arrays with nodes being adjacent to each other. so we can have [1,5,4] or [2,9,3,6] and so on. the length of each sub array does not matter.

But the trick is the sum part. we cannot just add all numbers, it works like flip flop. so for the sublist [2,9,3,6] the sum would be [2,-9,3,-6] which is: -10. and is pretty small. what would be the sublist (or sub-array if you like) of this array A that produces the maximum sum? one possible way would be (from intuition) that the sublist [4,2,9] will output a decent result : [4, -2, 9] = (add all the elements) = 11.

The question is, how to come up with a result like this? what is the sub-array that gives us the maximum flip-flop sum?

and mainly, what is the algorithm that takes any array as an input and outputs a sub-array with all numbers being adjacent and with the maximum sum?

I haven't come up with anything but I'm pretty sure I should pick either dynamic programming or divide and conquer to solve this issue. again, I don't know, I may be totally wrong.

Robur_131
  • 374
  • 5
  • 15
Sdowp
  • 29
  • 1

2 Answers2

3

The problem can indeed be solved using dynamic programming, by keeping track of the maximum sum ending at each position.

However, since the current element can be either added to or subtracted from a sum (depending on the length of the subsequence), we will keep track of the maximum sums ending here, separately, for both even as well as odd subsequence lengths.

The code below (implemented in python) does that (please see comments in the code for additional details). The time complexity is O(n).

a = [1, 5, 4, 2, 9, 3, 6]

# initialize the best sequences which end at element a[0]
# best sequence with odd length ending at the current position
best_ending_here_odd = a[0] # the sequence sum value
best_ending_here_odd_start_idx = 0
# best sequence with even length ending at the current position
best_ending_here_even = 0   # the sequence sum value
best_ending_here_even_start_idx = 1

best_sum = 0
best_start_idx = 0
best_end_idx = 0
for i in range(1, len(a)):
    # add/subtract the current element to the best sequences that
    # ended in the previous element
    best_ending_here_even, best_ending_here_odd = \
        best_ending_here_odd - a[i], best_ending_here_even + a[i]

    # swap starting positions (since a sequence which had odd length when it
    # was ending at the previous element has even length now, and vice-versa)
    best_ending_here_even_start_idx, best_ending_here_odd_start_idx = \
        best_ending_here_odd_start_idx, best_ending_here_even_start_idx

    # we can always make a sequence of even length with sum 0 (empty sequence)
    if best_ending_here_even < 0:
        best_ending_here_even = 0
        best_ending_here_even_start_idx = i + 1

    # update the best known sub-sequence if it is the case
    if best_ending_here_even > best_sum:
        best_sum = best_ending_here_even
        best_start_idx = best_ending_here_even_start_idx
        best_end_idx = i
    if best_ending_here_odd > best_sum:
        best_sum = best_ending_here_odd
        best_start_idx = best_ending_here_odd_start_idx
        best_end_idx = i

print(best_sum, best_start_idx, best_end_idx)

For the example sequence in the question, the above code outputs the following flip-flop sub-sequence:

4 - 2 + 9 - 3 + 6 = 14
danbanica
  • 3,018
  • 9
  • 22
  • would you please identify the merge, divide and conquer steps are? how can we analyse this and with what method to reach the complexity of O(n)? – Sdowp Aug 29 '19 at 02:33
  • 1
    @Aliz: the subproblems that are being solved in this dynamic approach are the following: "what are the best subsequences ending at element _i_, with odd, respectively even length", for every index _i_. This means we are solving 2 * n subproblems. But each such subproblem is solved in O(1), by using the solution for the previous _i_. Thus, the time complexity is O(n). – danbanica Aug 29 '19 at 08:32
1

As quertyman wrote, we can use dynamic programming. This is similar to Kadane's algorithm but with a few twists. We need a second temporary variable to keep track of trying each element both as an addition and as a subtraction. Note that a subtraction must be preceded by an addition but not vice versa. O(1) space, O(n) time.

JavaScript code:

function f(A){
  let prevAdd = [A[0], 1] // sum, length
  let prevSubt = [0, 0]
  let best = [0, -1, 0, null] // sum, idx, len, op
  let add
  let subt
  
  for (let i=1; i<A.length; i++){
    // Try adding
    add = [A[i] + prevSubt[0], 1 + prevSubt[1]]
  
    if (add[0] > best[0])
      best = [add[0], i, add[1], ' + ']
      
    // Try subtracting
    if (prevAdd[0] - A[i] > 0)
      subt = [prevAdd[0] - A[i], 1 + prevAdd[1]]
    else
      subt = [0, 0]
    
    if (subt[0] > best[0])
      best = [subt[0], i, subt[1], ' - ']
      
    prevAdd = add
    prevSubt = subt
  }
  
  return best
}

function show(A, sol){
  let [sum, i, len, op] = sol
  let str = A[i] + ' = ' + sum
  for (let l=1; l<len; l++){
    str = A[i-l] + op + str
    op = op == ' + ' ? ' - ' : ' + '
  }
  return str
}

var A = [1, 5, 4, 2, 9, 3, 6]
console.log(JSON.stringify(A))
var sol = f(A)
console.log(JSON.stringify(sol))
console.log(show(A, sol))

Update

Per OP's request in the comments, here is some theoretical elaboration on the general recurrence (pseudocode): let f(i, subtract) represent the maximum sum up to and including the element indexed at i, where subtract indicates whether or not the element is subtracted or added. Then:

// Try subtracting
f(i, true) =
  if f(i-1, false) - A[i] > 0
  then f(i-1, false) - A[i]
  otherwise 0

// Try adding
f(i, false) =
  A[i] + f(i-1, true)

(Note that when f(i-1, true) evaluates
 to zero, the best ending at
 i as an addition is just A[i])

The recurrence only depends on the evaluation at the previous element, which means we can code it with O(1) space, just saving the very last evaluation after each iteration, and updating the best so far (including the sequence's ending index and length if we want).

גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • Are you able to come up with a pseudocode for this or convert this into one? I'm struggling to understand the logic behind this since I'm very new to algorithms and programming world. – Sdowp Aug 29 '19 at 09:31