Search code examples
arraysalgorithmsortingdynamic-programminggreedy

How do I pick elements from two arrays such that their sum is minimum?


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.


Solution

  • 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]