-1

I have what seems like a simple problem, but I'm struggling to solve it. Turning to Google it appears this may be a variation of the Knapsack problem, but I'm having trouble mapping those solutions to this particular problem.

Let's say I have two lists of positive integers, A and B. I want to find the value that represents the largest common sum between these two lists.

A: [6, 1]
B: [5, 3, 1]

Here, the answer is 6, because that's the largest sum that can be created commonly in both lists (by removing the 1 in list A and removing the 3 in list B).

I can naively solve this in O(2^n) but I'm assuming there's a much more efficient approach via dynamic programming, though dp is not my strength.

Is this the knapsack problem? Any pointers as to how I should map the classic knapsack problem to this two-list problem?

ricklane
  • 173
  • 1
  • 11

1 Answers1

3

This can be solved in O(nW), where n is count of all elements and W is a sum of all elements in lists.

Process each list separately. Let dp1[i][j] be true if there is a subset of the first i elements of the first list, which sum is equal to j (0 <= i <= n1, 0 <= j <= W1). dp1 can be filled using recurrent formula:

  • dp1[0][0] is true
  • dp1[0][j] is false (j != 0)
  • EDIT: dp1[i][0] is true (i != 0)
  • dp1[i][j] is true if:
    • (j >= list.get(i) AND dp[i - 1][j - list.get(i)] is true)
    • OR dp[i - 1][j] is true

Then fill dp2[i][j] for the second list. Then just find the maximum number S for which both dp1[n1][S] and dp2[n2][S] are true.

ardenit
  • 3,610
  • 8
  • 16
  • Wow, thank you @ardenit. Out of curiosity, is this a variant of the knapsack problem? – ricklane Feb 08 '20 at 15:36
  • 2
    Well, this solution is very similar to solution of subset sum problem, so it may be counted as a variation of knapsack problem. – ardenit Feb 08 '20 at 15:44
  • this could be an implementation error on my part, but I believe your algorithm doesn't allow for taking an item from one of the lists without taking all preceding items. In my original question, if I had reordered list A, i.e. A: [1, 6], the answer should still be 6. But with your solution, there is no way to obtain a value of 6 from list A (since taking the 2nd element assumes you're also taking the 1st element). – ricklane Feb 09 '20 at 20:45
  • Are you sure that you implemented this line -> `OR dp[i - 1][j] is true` – ardenit Feb 09 '20 at 20:47
  • Yes, I implemented that line. I'm struggling with understanding what the meaning of `dp[i][j]` is -- originally, I assumed it meant a value of true for i, j indicated a value of j could be achieved by taking i elements from the list. But I no longer think that's what dp represents. If you apply your algorithm to List=[1, 6] what is your resulting dp? – ricklane Feb 09 '20 at 22:11
  • The only way for `dp[i - 1][6]` to be true for that list is for the previous check, `dp[i - 1][j - list.get(i)]` to populate `true`. And since 6 is the second element in the list (`i == 2`), `dp[1][0]` is not `true` – ricklane Feb 09 '20 at 22:17
  • You misunderstand what `dp` means. As I said, `dp[i][j]` is true if there is a subset (any subset, of any size) of the first `i` elements of list, with sum `j`. – ardenit Feb 09 '20 at 22:24
  • Ah, there is a stupid mistake in my algorithm, I fixed it. Although it was about one of base cases (`j = 0`), not about the algorithm itself. For your example, `dp[2][6]` is `true` because `dp[1][0]` is `true`. – ardenit Feb 09 '20 at 22:30
  • And `list.get(i)` in this algorithm means "get i-th element of the list" (`list.get(1)` returns the first element) – ardenit Feb 09 '20 at 22:32
  • Ahh ironically I was questioning whether that should be `true` and not `false` but I couldn't put my finger on why that would work. But makes sense now. – ricklane Feb 09 '20 at 23:14