4

I have two arrays of equal length filled with integers(could be positive or negative but never 0). At each index I can either choose the element of array1 or from array2, and the absoulute value of the sum of such elements should be minimum.

Eg:

a1 = [2, 2, 1]
a2 = [-3, -3, -4]

The correct answer would be to choose like this:

At index 0 : -3 from a2
At index 1 : 2 from a1
At index 2 : 1 from a1

Thus, the final sum would be 0.

xashru
  • 3,400
  • 2
  • 17
  • 30
  • 1
    What is your approach then? – nice_dev Feb 03 '19 at 18:00
  • You can do it as either top-down or bottom-up. – nice_dev Feb 03 '19 at 18:00
  • My approach was to take the element with the minimum absolute value from both arrays which works for a lot of cases but fails in the example that I've shown in the question. Can this be done without dp? – Distinctly Average Feb 03 '19 at 18:05
  • `Can this be done without dp`? Probably(maybe improving on space). However, can you give more examples, since `absoulute value of the sum` is a bit unclear to me. – nice_dev Feb 03 '19 at 18:22
  • Sure, a1 = [2, 1, 1, -1] a2 = [-1, -2, -2, -4] ans = [-1, 1, 1, -1] Btw, what I want to minimize is pos + abs(neg + pos). Here, pos and neg are the sums of positive and negative values of the final array that will be formed after picking the ideal elements from both the orginal elements. This is why I picked -3, 2, 1 over 2, 2, -4. – Distinctly Average Feb 03 '19 at 18:27
  • what about `0`, does it count as pos or neg? – Indominus Feb 03 '19 at 18:51
  • does this mean your correct output should be 3 for the example abs(-3+2+1) + (2+1)? If so please update your statement. – xashru Feb 03 '19 at 18:54
  • @DistinctlyAverage This is still unclear seeing your comments. Could you add more examples of `a1` and `a2` with their solutions? – nice_dev Feb 03 '19 at 18:59
  • @xashru, actually, there are still multiple solution, it can also be (2, -3, 1). I will update the details. – Indominus Feb 03 '19 at 18:59

3 Answers3

2

First, simplify the question:

  • Create array b, where b[i] = a1[i] - a2[i].
  • sumA1 = sum of each elements in a1.

Then the problem becomes:

Find a sub array from b, mark as c, mark its sum as sumC which should be closest to sumA1.

Or, you can also say it should have minimal Math.abs(sumC - sumA1).

BTW, if c is empty, it's also valid, which means choose all indices from a1.

Then this question is similar to this one: Given an input array find all subarrays with given sum K

Or, refer to this article:

And, to go back to OP's question:

  • Any indices picked in b are for a2.
  • Any indices not picked in b are for a1.
Community
  • 1
  • 1
Eric
  • 22,183
  • 20
  • 145
  • 196
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/187910/discussion-on-answer-by-eric-wang-how-do-i-pick-elements-from-two-arrays-such-th). – Bhargav Rao Feb 05 '19 at 08:49
1

Here is a dynamic programming solution that finds the minimum value for pos + abs(neg + pos)(as per OP's update) and prints one candidate solution. We need to save both total sum and sum of positive integers as dp state to find the minimum. I am not sure if we can solve it without the pos dimension. Time complexity is O(#elements * (sum of absolute values of elements)^2). Of course if the individual numbers are very large this is not a feasible solution. In that case the brute force approach will work when number of elements is ~20.

a1 = [2, 1, 1, -1] 
a2 = [-1, -2, -2, -4]
memo = {}   # to store dp state
nxt = {}    # for reconstructing path

def foo(a1, a2, index, total, pos):
    if index == len(a1):
        return pos + abs(total)
    if (index, total, pos) in memo:
        return memo[(index, total, pos)]

    # take from first array
    if a1[index] > 0:
        r1 = foo(a1, a2, index+1, total + a1[index], pos+a1[index])
    else:
        r1 = foo(a1, a2, index+1, total + a1[index], pos)

    # take from second array
    if a2[index] > 0:
        r2 = foo(a1, a2, index+1, total + a2[index], pos+a2[index])
    else:
        r2 = foo(a1, a2, index+1, total + a2[index], pos)

    # save path taken at this step
    if r1 < r2:
        nxt[index] = 0
    else:
        nxt[index] = 1

    memo[index, total, pos] = min(r1, r2)
    return min(r1, r2)

print('minimum sum:', foo(a1, a2, 0, 0, 0))   # minimum sum: 2
# path reconstruction
path = []
node = 0
while node < len(a1):
    path.append(nxt[node])
    node += 1
print('path:', path)   # path: [1, 0, 0, 0]
xashru
  • 3,400
  • 2
  • 17
  • 30
0
import itertools as iter
a = [a1, a2]
p = len(a1)
idx_to_pick = min(iter.product(*([[0, 1]]*p)), 
                  key=lambda b: abs(sum([a[i][j] for i, j in zip(b, range(p))])))

this code suggest pick a1[0] + a1[1] + a2[2] = 2 + 2 + (-4), different from OP's pick, but is also correct.

Update per OP's follow up question in comment to this answer:

import itertools as iter
a1 = [2, 2, 1]
a2 = [-3, -3, -4]
a = [a1, a2]
p = len(a1)


def obj_func(b):
    arr = [a[i][j] for i, j in zip(b, range(p))]
    return sum([x for x in arr if x > 0]) + abs(sum(arr))


idx_to_pick = min(iter.product(*([[0, 1]]*p)), key=obj_func)

With the new objective function, there are still multiple solutions. It can be (-3, 2, 1) or (2, -3, 1)

Indominus
  • 1,228
  • 15
  • 31
  • That's basically brute force, isn't it? Since I don't mind finding a brute force solution, can you help me extend this to where I want to minimize this value: pos + abs(neg + pos) Here, pos and neg are the sums of positive and negative values of the final array that will be formed after picking the ideal elements from both the orginal elements. This is why I picked -3, 2, 1 over 2, 2, -4. – Distinctly Average Feb 03 '19 at 18:23
  • sure, just be clearly, basically what you want to minimize is `sum(all the positive values in the final array) + abs(sum(all values in array))`? – Indominus Feb 03 '19 at 18:46
  • @DistinctlyAverage, what about `0`, does it count as pos or neg? If neither, then neg+pos does not equal the sum of all. – Indominus Feb 03 '19 at 18:56
  • 1
    @Indominus Problem statement says `could be positive or negative but never 0` – nice_dev Feb 03 '19 at 18:57