Finding 2 equal sum sub-sequences, with maximum sum?
Idea of your second approach is correct, it's basically a reduction to the knapsack problem. However, it looks like your code lacks clear contract: what the recurse
function is supposed to do.
Here is my suggestion: int recurse(int idx, int sum)
distributes elements on positions idx..n-1
into three multisets A
, B
, C
such that sum+sum(A)-sum(B)=0
and returns maximal possible sum(A)
, -inf
otherwise (here -inf
is some hardcoded constant which serves as a "marker" of no answer; there are some restrictions on it, I suggest -inf == -1000
).
Now you're to write a recursive backtracking using that contract and then add memoization. Voila—you've got a dynamic programming solution.
In recursive backtracking we have two distinct situations:
- There are no more elements to distribute, no choices to make:
idx == n
. In that case, we should check that our condition holds (sum + sum(A) - sum(B) == 0
, i.e.sum == 0
) and return the answer. Ifsum == 0
, then the answer is 0. However, ifsum != 0
, then there is no answer and we should return something which will never be chosen as the answer, unless there are no answer for the whole problem. As we modify returning value ofrecurse
and do not want extraif
s, it cannot be simply zero or even-1
; it should be a number which, when modified, still remains "the worst answer ever". The biggest modification we can make is to add all numbers to the resulting value, hence we should choose something less or equal to negative maximal sum of numbers (i.e.-1000
), as existing answers are always strictly positive, and that fictive answer will always be non-positive. - There is at least one remaining element which should be distributed to either
A
,B
orC
. Make the choice and choose the best answer among three options. Answers are calculated recursively.
Here is my implementation:
const int MAXN = 50;
const int MAXSUM = 1000;
bool visited[MAXN + 1][2 * MAXSUM + 1]; // should be filled with false
int dp[MAXN + 1][2 * MAXSUM + 1]; // initial values do not matter
int recurse(int idx, int sum){
// Memoization.
if (visited[idx][sum + MAXSUM]) {
return dp[idx][sum + MAXSUM];
}
// Mark the current state as visited in the beginning,
// it's ok to do before actually computing it as we're
// not expect to visit it while computing.
visited[idx][sum + MAXSUM] = true;
int &answer = dp[idx][sum + MAXSUM];
// Backtracking search follows.
answer = -MAXSUM; // "Answer does not exist" marker.
if (idx == N) {
// No more choices to make.
if (sum == 0) {
answer = 0; // Answer exists.
} else {
// Do nothing, there is no answer.
}
} else {
// Option 1. Current elemnt goes to A.
answer = max(answer, arr[idx] + recurse(idx + 1, sum + arr[idx]));
// Option 2. Current element goes to B.
answer = max(answer, recurse(idx + 1, sum - arr[idx]));
// Option 3. Current element goes to C.
answer = max(answer, recurse(idx + 1, sum));
}
return answer;
}