You are given an integer array nums
. The adjacent integers in nums
will perform the float division.
nums = [2,3,4]
, we will evaluate the expression "2/3/4"
.However, you can add any number of parenthesis at any position to change the priority of operations. You want to add these parentheses such the value of the expression after the evaluation is maximum.
Return the corresponding expression that has the maximum value in string format.
Note: your expression should not contain redundant parenthesis.
Example 1:
Input: nums = [1000,100,10,2] Output: "1000/(100/10/2)" Explanation: 1000/(100/10/2) = 1000/((100/10)/2) = 200 However, the bold parenthesis in "1000/((100/10)/2)" are redundant since they do not influence the operation priority. So you should return "1000/(100/10/2)". Other cases: 1000/(100/10)/2 = 50 1000/(100/(10/2)) = 50 1000/100/10/2 = 0.5 1000/100/(10/2) = 2
Example 2:
Input: nums = [2,3,4] Output: "2/(3/4)" Explanation: (2/(3/4)) = 8/3 = 2.667 It can be shown that after trying all possibilities, we cannot get an expression with evaluation greater than 2.667
Constraints:
1 <= nums.length <= 10
2 <= nums[i] <= 1000
This problem asks to find the optimal parenthesization of a division expression to maximize the result. Surprisingly, the solution doesn't require complex dynamic programming or exhaustive search. The optimal solution is always to group all numbers after the first one in the denominator.
Mathematical Intuition
Consider an example: [a, b, c, d]
. We want to maximize a / (b / (c / d))
. The key observation is that dividing by a smaller number yields a larger result. By grouping b, c, d
in the denominator, we minimize the denominator and maximize the overall result. This holds true regardless of the number of elements.
Algorithm
Base Cases:
a/b
.General Case:
a/(b/c/d...)
, where a
is the first number and b, c, d...
are the remaining numbers, grouped in the denominator. This ensures the denominator is minimized, maximizing the overall result.Time and Space Complexity
Code Implementation (Python)
class Solution:
def optimalDivision(self, nums: List[int]) -> str:
n = len(nums)
if n == 1:
return str(nums[0])
if n == 2:
return str(nums[0]) + "/" + str(nums[1])
result = str(nums[0]) + "/(" + "/".join(map(str, nums[1:])) + ")"
return result
Code Implementation (Java)
class Solution {
public String optimalDivision(int[] nums) {
int n = nums.length;
if (n == 1) return String.valueOf(nums[0]);
if (n == 2) return nums[0] + "/" + nums[1];
StringBuilder sb = new StringBuilder(nums[0] + "/(");
for (int i = 1; i < n; i++) {
sb.append(nums[i]);
if (i < n - 1) sb.append("/");
}
sb.append(")");
return sb.toString();
}
}
Code Implementation (C++)
class Solution {
public:
string optimalDivision(vector<int>& nums) {
int n = nums.size();
if (n == 1) return to_string(nums[0]);
if (n == 2) return to_string(nums[0]) + "/" + to_string(nums[1]);
string result = to_string(nums[0]) + "/(";
for (int i = 1; i < n; ++i) {
result += to_string(nums[i]);
if (i < n - 1) result += "/";
}
result += ")";
return result;
}
};
The other languages (Go, TypeScript, Rust) follow a similar pattern, adapting the string manipulation to their respective syntax and libraries. The core logic of handling the base cases and creating the optimal string remains consistent.