Reputation: 1273
I have expression consisting of numbers separated with plus and minus sign. I need to get the maximal result of this expression by putting braces between the numbers. I'm trying to get polynomial algorithm for this problem, but I need some advice or hint how to achieve it. I've found something similar here, but I don't know how to modify it.
EDIT: I was thinking that the idea could be similar like this
getMax(inp)
{
if(|inp| == 1)
return inp[1] // base case
else
val = 0;
for(k=2; k < |inp|; k+=2)
val = max{val, getMax(inp[:k]) + getMax(inp[k+1:])}
}
Upvotes: 2
Views: 206
Reputation: 10575
One strategy is to use dynamic programming to choose the best operation to perform last. This divides the expression in two parts.
If the operation is addition, you call recursively on each part to find the maximum for each part.
If the operation is subtraction, you want to find the maximum on the first part and the minimum on the second part.
Here is some non-memoized code, just to show how the recurrence works (note that i
iterates only on the indices of the operations, to choose the best place to break the expression):
import re
def T(s, f1=max, f2=min):
if len(s) == 1:
return int(s[0])
return f1(
T(s[:i], f1, f2)+T(s[i+1:], f1, f2)
if s[i]=='+' else
T(s[:i], f1, f2)-T(s[i+1:], f2, f1)
for i in xrange(1, len(s), 2))
def solve(expr):
return T(re.split('([+-])', expr))
print solve('1-2+1') #0 ((1-2)+1)
print solve('1-22-23') #2 (1-(22-23))
Implementing a bottom-up dynamic programming is a little more tricky, as the ideal order to fill the table is somewhat non-conventional. The easiest way is to make the DP around T(k, i)
that denotes "maximum/minimum for expressions of k
operands starting at the i
th operand". Using Anonymous idea of separating operators and numbers in O
and N
respectively, an example code would be:
import re
def T(O, N):
n1 = len(N)+1 #maximum expression length
Tmax = [[float('-Inf')]*len(N) for _ in xrange(n1)]
Tmin = [[float('+Inf')]*len(N) for _ in xrange(n1)]
for i, n in enumerate(N): #only the numbers
Tmax[1][i] = Tmin[1][i] = int(n)
for k in xrange(2, n1):
for i in xrange(n1-k):
for j in xrange(1, k):
if (O[i+j-1] == '+'):
Tmax[k][i] = max(Tmax[k][i], Tmax[j][i]+Tmax[k-j][i+j])
Tmin[k][i] = min(Tmin[k][i], Tmin[j][i]+Tmin[k-j][i+j])
else:
Tmax[k][i] = max(Tmax[k][i], Tmax[j][i]-Tmin[k-j][i+j])
Tmin[k][i] = min(Tmin[k][i], Tmin[j][i]-Tmax[k-j][i+j])
return Tmax[len(N)][0]
def solve(expr):
A = re.split('([+-])', expr)
return T(A[1::2], A[::2])
print solve('1+1') #2
print solve('1-2+1') #0 ((1-2)+1)
print solve('1-22-23') #2 (1-(22-23))
Upvotes: 5
Reputation: 58339
Let the operators be O[0]
, O[1]
, ..., O[K-1]
. Let the numbers be N[0]
, N[1]
, ..., N[K]
. (There's one more number than operator).
Let M[op, i, j]
be the largest value achievable from the sub-expression starting from number i
and ending from number j
(inclusive, both ends) if op
is +
, and the smallest value if op
is -
.
Thus M[+, 0, K]
is maximum value the whole expression can take.
M
satisfies a recurrence relation:
M[+, i, i] = M[-, i, i] = N[i]
M[+, i, j] = max(M[+, i, k] O[k] M[O[k], k+1, j) for k in i...j-1)
M[-, i, j] = min(M[-, i, k] O[k] M[-O[k], k+1, j) for k in i...j-1)
Here A O[k] B
means A + B
or A - B
depending on O[k]
, and -O[k]
means -
if O[k]
is +
, and +
if O[k]
is -
.
Basically, you're trying to find the best place to split the expression to either maximise or minimise the overall result. When you consider a -
operator, you switch from maximising to minimising and vice versa on the right-hand-side.
These recurrence relations can be evaluated using dynamic programming in a direct way, by building a 3 dimensional table for M
of size 2 * (K+1) * (K+1), where K is the number of operators.
Overall, this algorithm is O(K^3).
Upvotes: 2