The proposed solution is proposed here:
def max_result(a_): memo = {} a = list(a_) a.insert(0, 0) return min_and_max(a, 0, len(a)-1, memo)[1] def min_and_max(a, i, j, memo): if (i, j) in memo: return memo[i, j] if i == j: return (a[i], a[i]) min_val = max_val = None for k in range(i, j): left = min_and_max(a, i, k, memo) right = min_and_max(a, k+1, j, memo) for op in "*-+": for x in left: for y in right: val = apply(x, y, op) if min_val == None or val < min_val: min_val = val if max_val == None or val > max_val: max_val = val ret = (min_val, max_val) memo[i, j] = ret return ret def apply(x, y, op): if op == '*': return x*y if op == '+': return x+y return xy
max_result is the main function, and min_and_max is the helper. The latter returns the minimum and maximum results that can be achieved using the subsequence a [i..j].
It is assumed that the maximum and minimum results of the sequences are composed of the maximum and minimum results of the subsequences. Under this assumption, the problem has an optimal substructure and can be solved using dynamic programming (or memoization). Operating time O (n ^ 3).
I did not prove the correctness, but I checked its conclusion against a brute force solution with thousands of small random generated inputs.
It handles the possibility of getting the unary minus by inserting zero at the beginning of the sequence.
EDIT
I thought a little more about this problem, and I believe that it can be reduced to a simpler problem, in which all values are (strictly) positive, and only the * and + operators are allowed.
Just remove all zeros from the sequence and replace the negative numbers with their absolute value.
In addition, if there is not one in the resulting sequence, the result will be just the product of all numbers.
After this reduction, a simple dynamic programming algorithm will work.
EDIT 2
Based on previous views, I think I found a linear solution:
def reduce(a): return filter(lambda x: x > 0, map(abs, a)) def max_result(a): b = reduce(a) if len(b) == 0: return 0 return max_result_aux(b) def max_result_aux(b): best = [1] * (len(b) + 1) for i in range(len(b)): j = i sum = 0 while j >= 0 and ij <= 2: sum += b[j] best[i+1] = max(best[i+1], best[j] * sum) j -= 1 return best[len(b)]
best [i] - the maximum result that can be achieved by the subsequence b [0 .. (i-1)].
EDIT 3
Here is an argument in favor of the O(n) algorithm, based on the following assumption:
You can always achieve maximum results with an expression of the form
+/- (a_1 +/- ... +/- a_i) * ... * (a_j +/- ... +/- a_n)
That is: a product of factors consisting of an algebraic sum of terms (including the case of only one factor).
I will also use the following lemmas, which are easy to prove:
Lemma 1: x*y >= x+y for all x,y such that x,y >= 2
Lemma 2: abs(x_1) + ... + abs(x_n) >= abs(x_1 +/- ... +/- x_n)
Here it is.
The sign of each factor does not matter, because you can always make a product positive using the leading unary minus. Therefore, to maximize the product, we need to maximize the absolute value of each factor.
Leaving aside the trivial case when all numbers are zeros, in the optimal solution, not a single factor will consist only of zeros. Therefore, since zeros do not affect every sum of members, and each factor will have at least one nonzero number, we can remove all zeros. From now on, suppose that there are no zeros.
Let the concentration in each amount be expressed separately:
(x_1 +/- x_2 +/- ... +/- x_n)
By virtue of Lemma 2, the maximum absolute value that each factor can obtain is the sum of the absolute values of each term. This can be achieved as follows:
If x_1 is positive, add all the positive members and subtract all the negative members. If x_1 is negative, subtract all the positive members and add all the negative members.
This means that the sign of each term does not matter, we can consider the absolute value of each number and use only the operator + internal factors. From now on, let all numbers be positive.
The most important step that leads to the O(n) algorithm is the proof that the maximum result can always be achieved with factors that have no more than three members.
Suppose that we have a coefficient of more than three members, by Lemma 1 we can divide it into two smaller factors of 2 or more members each (therefore, each of them can contain up to 2 or more) without reducing the overall result. We can break it down several times until factors remain in more than 3 terms.
This completes the argument. I still have not found a full justification for the initial assumption. But I checked my code with millions of randomly generated cases and couldn't break it.