# maximum expression by adding parentheses
## problem:
Describe and analyze an algorithm to compute, given a list of integers separated by + and − signs, the maximum possible value the expression can take by adding parentheses. Parentheses must be used only to group additions and subtractions; in particular, do not use them to create implicit multiplication.
## algorithm:
Similar to matrix chain multiplication, we can utilize interval dynamic programming. Define nums[i] as the ith number and ops[i] as the operator (either + or -) between nums[i] and nums[i + 1]. Then define two DP tables, maxVal[i][j] as the maximum value we can get the subexpression nums[i] to nums[j] and minVal[i][j] as the minimum value from the subexpression. Two tables are necessary as the subtraction operator can turn a large negative into a large positive and must be considered.
The base case when there is only a single element in the subexpression is maxVal[i][i] = minVal[i][i] = nums[i]. For every possible split point k between i and j for each larger range [i...j], subexpression = (nums[i] ... nums[k]) op[k] (nums[k + 1] ... nums[k]). Then, combine according to op[k]. If op[k] == '+', maxVal[i][j] = max(maxVal[i][j], maxVal[i][k] + maxVal[k+1][j]) and minVal[i][j] = min(minVal[i][j], minVal[i][k] + minVal[k+1][j]), else if op[k] == '-' maxVal[i][j] = max(maxVal[i][j], maxVal[i][k] - minVal[k+1][j]) and minVal[i][j] = min(minVal[i][j], minVal[i][k] - maxVal[k+1][j]).
""
```javascript=
// input: array of numbers and array of operators between those numbers
// output: the maximum value of the expression through addition of parentheses
int maxExpressionValue(vector<int> nums, vector<char> ops) {
int n = nums.size();
// create 2d arrays
vector<vector<int>> maxVal(n, vector<int>(n));
vector<vector<int>> minVal(n, vector<int>(n));
// base cases
for (int i = 0; i < n; i++) {
maxVal[i][i] = minVal[i][i] = nums[i];
}
// length of subexpression
for (int length = 2; length <= n; length++) {
for (int i = 0; i + length - 1 < n; i++) {
int j = i + length - 1;
// initalize maximum and minimums
maxVal[i][j] = (int minimum);
minVal[i][j] = (int maximum);
for (int k = i; k < j; k++) {
if (ops[k] == '+') {
maxVal[i][j] = max(maxVal[i][j], maxVal[i][k] + maxVal[k+1][j]);
minVal[i][j] = min(minVal[i][j], minVal[i][k] + minVal[k+1][j]);
}
// else ops[k] == '-'
else {
maxVal[i][j] = max(maxVal[i][j], maxVal[i][k] - minVal[k+1][j]);
minVal[i][j] = min(minVal[i][j], minVal[i][k] - maxVal[k+1][j]);
}
}
}
}
return maxVal[0][n-1];
}
// sample call
// standard input: 1 + 3 - 2 - 5 + 1 - 6 + 7
// separate into two arrays (not relevant to scope of problem):
// vector<int> nums = {1, 3, 2, 5, 1, 6, 7}
// vector<char> ops = {+, -, -, +, -, +}
// int maxExpressionValue(nums, ops) // expect 9
```
## correctness:
#### Theorem:
The algorithm correctly identifies both the true maximum and minimum value after parenthesization for any input.
#### Proof:
Proof by induction on length l = j - i + 1.
Base case (l = 1): When i = j, the subexpression is a single number, meaning that the only parenthesization trivially yields the value nums[i]. The algorithm accounts for this in this line by setting maxVal[i][i] = minVal[i][i] = nums[i], which means it correctly identifies the true maximum and minimum values.
Inductive hypothesis: Assume for all intervals of length < l, the algorithm correctly computes maxVal and minVal.
Inductive step (prove for all intervals of length ≥ 2): Fix an interval [i, j] with j - i + 1 = l. Consider any full parenthesization of the subexpression. Any such one has a top-level binary split: there exists some k with i ≤ k < j s.t. the topmost operation combines a left subexpression over [i...k] and a right one over [k + 1...j], and that top operator is op[k].
Therefore every element/value v of E(i, j) can be written as either v = x + y, where x is an element of E(i, k), y is an element of E(k + 1, j) when op[k] = '+', or v = x - y where x is an element of E(i, k), y is an element of E(k + 1, j) when op[k] = '-'. Thus, the set of all possible values E(i,j) is the union of the results you can form by taking any possbile result from the left part [i, k] and combining it (with '+' or '-') with any possible result from the right. We want the max and min of that union
By the inductive hypothesis, the sets E(i, k) and E(k + 1, j) correctly represent their minimums and maximums. Consider two cases:
- Case 1: op[k] = '+'
For fixed k, the set {x + y : x ∈ E(i,k), y ∈ E(k+1,j)} has maximum maxVal[i][k] + maxVal[k + 1][j] and minimum minVal[i][k] + minVal[k + 1][j] because, in each argument, addition is monotone. Thus, the largest value obtainable using split k is maxVal[i][k] + maxVal[k+1][j] and the smallest is minVal[i][k] + minVal[k+1][j].
- Case 2: op[k] = '-'
For fixed k, the set {x − y : x ∈ E(i,k), y ∈ E(k+1,j)} has maximum maxVal[i][k] − minVal[k+1][j] and minimum minVal[i][k] − maxVal[k+1][j]. This continues to follow the monotonicity and the observation that x - y increases with x and decreases with y.
Combining over all k, the global maximum over E(i, j) is the maximum of the k-specific maxima and the minimum is the minimum of the k-specific minima. If op[k] = '+', candidateMax = maxVal[i][k] + maxVal[k + 1][j], candidateMin = minVal[i][k] + minVal[k + 1][j], and if op[k] = '-', candidateMax = maxVal[i][k] − minVal[k + 1][j], candidateMin = minVal[i][k] − maxVal [k + 1][j]. Taking the respective maximum/minimum of all candidateMax/candidateMin over k yields the true maximum/minimum for i, j. Thus, the DP correctly comptues maxVal[i][j] and minVal[i][j] for length l, completing the inductive step.
By induction, the algorithim is correctl for all intervals, in which maxVal[1][n] is the maximum value of the whole expression. QED.
## complexity:
The time complexity is the same as standard optimal parenthesization dynamic programming problem, O(n^3). This is because there are O(n^2) subproblems and each subproblem checks O(n) splits.