The Optimal Division problem asks you to arrange a given list of positive integers, nums
, into a mathematical expression using only division operators, such that the resulting value is maximized. The division must follow the standard left-to-right order unless parentheses are used to override precedence. You must return the expression as a string, inserting parentheses in such a way that the result is as large as possible.
nums
of integers (length ≥ 1)
Example:
Input: nums = [1000, 100, 10, 2]
Output: "1000/(100/10/2)"
At first glance, it might seem like you would need to try all possible ways of inserting parentheses between the numbers to maximize the result, which would be computationally expensive. However, by analyzing how division works, we can reason about how to maximize the result:
This leads to the insight that the optimal solution always places parentheses around nums[1] / nums[2] / ... / nums[n-1]
, making the denominator as small as possible.
The solution can be broken down into the following steps:
nums
:
nums[0] + "/(" + nums[1] + "/" + ... + nums[n-1] + ")"
.This approach works because, mathematically, grouping the denominator in this way always minimizes it, thus maximizing the overall value.
Let's take the input nums = [1000, 100, 10, 2]
and walk through the solution:
1000
.
100/10/2
, grouped with parentheses: (100/10/2)
.
1000/(100/10/2)
.
1000/100/10/2
(which is much smaller).1000/(100/10/2) = 1000/(5) = 200
1000/100/10/2 = 10/10/2 = 1/2 = 0.5
Clearly, the parenthesized version yields a much larger result.
O(C_n)
where C_n
is the Catalan number for n, which is exponential.
O(n)
and space complexity is also O(n)
for the output string.
The key insight in the Optimal Division problem is realizing that grouping all but the first number in the denominator with parentheses always yields the largest possible result. This reduces a potentially exponential search problem to a simple string construction in linear time. The elegance of this solution lies in recognizing the mathematical properties of division and exploiting them to avoid brute-force computation.
class Solution:
def optimalDivision(self, nums):
if not nums:
return ""
if len(nums) == 1:
return str(nums[0])
if len(nums) == 2:
return str(nums[0]) + "/" + str(nums[1])
denominator = "/".join(str(num) for num in nums[1:])
return str(nums[0]) + "/(" + denominator + ")"
class Solution {
public:
string optimalDivision(vector<int>& nums) {
if (nums.empty()) return "";
if (nums.size() == 1) return to_string(nums[0]);
if (nums.size() == 2) return to_string(nums[0]) + "/" + to_string(nums[1]);
string result = to_string(nums[0]) + "/(";
for (int i = 1; i < nums.size(); ++i) {
result += to_string(nums[i]);
if (i != nums.size() - 1) result += "/";
}
result += ")";
return result;
}
};
class Solution {
public String optimalDivision(int[] nums) {
if (nums.length == 0) return "";
if (nums.length == 1) return Integer.toString(nums[0]);
if (nums.length == 2) return nums[0] + "/" + nums[1];
StringBuilder sb = new StringBuilder();
sb.append(nums[0]).append("/(");
for (int i = 1; i < nums.length; i++) {
sb.append(nums[i]);
if (i != nums.length - 1) sb.append("/");
}
sb.append(")");
return sb.toString();
}
}
var optimalDivision = function(nums) {
if (nums.length === 0) return "";
if (nums.length === 1) return nums[0].toString();
if (nums.length === 2) return nums[0] + "/" + nums[1];
return nums[0] + "/(" + nums.slice(1).join("/") + ")";
};