Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

553. Optimal Division - Leetcode Solution

Problem Description

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.

  • Input: An array nums of integers (length ≥ 1)
  • Output: A string representation of the optimal division expression
  • Constraints: All numbers are positive integers. The order of numbers cannot be changed, but you can add parentheses anywhere.
  • Each number must be used exactly once, and only division is allowed.

Example:
Input: nums = [1000, 100, 10, 2]
Output: "1000/(100/10/2)"

Thought Process

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:

  • Division is not associative, so the order and grouping (parentheses) matter.
  • To maximize the result, you want the numerator as large as possible and the denominator as small as possible.
  • Since you cannot change the order of the numbers, the first number will always be in the numerator.
  • To minimize the denominator, you want to divide the second number by all the remaining numbers grouped together, i.e., the denominator becomes as small as possible.

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.

Solution Approach

The solution can be broken down into the following steps:

  1. Check the length of nums:
    • If there is only one number, return it as a string.
    • If there are two numbers, return them joined by a division sign.
  2. For more than two numbers:
    • Place the first number as the numerator.
    • Group all the remaining numbers in the denominator with parentheses.
    • Join the denominator numbers with division signs.
    • Return the expression as a string in the form 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.

Example Walkthrough

Let's take the input nums = [1000, 100, 10, 2] and walk through the solution:

  1. There are 4 numbers, so we follow the general approach for more than two numbers.
  2. The numerator is 1000.
  3. The denominator is 100/10/2, grouped with parentheses: (100/10/2).
  4. The final expression is 1000/(100/10/2).
  5. Without parentheses, the expression would be 1000/100/10/2 (which is much smaller).
  6. Evaluating both:
    • 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.

Time and Space Complexity

  • Brute-force approach: If you tried all possible ways of inserting parentheses, the number of possibilities grows exponentially with the number of numbers (like generating all binary trees). This would be O(C_n) where C_n is the Catalan number for n, which is exponential.
  • Optimized approach: With the mathematical insight, we only need to construct the string in linear time. We loop through the list once to build the denominator part, so the time complexity is O(n) and space complexity is also O(n) for the output string.

Summary

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.

Code Implementation

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("/") + ")";
};