Formatted question description: https://leetcode.ca/all/553.html

553. Optimal Division (Medium)

Given a list of positive integers, the adjacent integers will perform the float division. For example, [2,3,4] -> 2 / 3 / 4.

However, you can add any number of parenthesis at any position to change the priority of operations. You should find out how to add parenthesis to get the maximum result, and return the corresponding expression in string format. Your expression should NOT contain redundant parenthesis.

Example:

Input: [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 don't 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

Note:

  1. The length of the input array is [1, 10].
  2. Elements in the given array will be in range [2, 1000].
  3. There is only one optimal division for each test case.

Companies:
Amazon

Related Topics:
Math, String

Solution 1. Brute Force + Memo

The goal is to make the numerator as great as possible, and the denominator as small as possible.

Assume F(x, y, z, ...) is the greatest division we can get out of x, y, z, ..., and G(x, y, z, ...) is the smallest division we can get.

F(x) = G(x) = x

F(x, y) = G(x, y) = x / y

F(x, y, z) = max(
  F(x, y) / z,
  x / G(y, z))
G(x, y, z) = min(
  G(x, y) / z,
  x / F(y, z))

...

So we can compute F and G from small scale to large scale. F(...nums) is the answer.

// OJ: https://leetcode.com/problems/optimal-division/

// Time: O(N^3)
// Space: O(N^3)
class Pair {
private:
    bool isNum;
    int num;
    Pair *numerator, *denominator;
    float val;
public:
    Pair(int n) : isNum(true), num(n), val(n) {}
    Pair(Pair* n, Pair* d) : isNum(false), numerator(n), denominator(d), val(numerator->val / denominator->val) {}
    float value() { return val; }
    string toString() {
        if (isNum) return to_string(num);
        string ans;
        ans = numerator->toString() + "/";
        if (!denominator->isNum) ans += "(";
        ans += denominator->toString();
        if (!denominator->isNum) ans += ")";
        return ans;
    }
};
class Solution {
public:
    string optimalDivision(vector<int>& nums) {
        unordered_map<int, unordered_map<int, Pair*>> great, small;
        for (int i = 0; i < nums.size(); ++i)
            great[i][i + 1] = small[i][i + 1] = new Pair(nums[i]);
        for (int len = 2; len <= nums.size(); ++len) {
            for (int i = 0; i < nums.size() - len + 1; ++i) {
                Pair *greatest = NULL, *smallest = NULL;
                for (int j = i + 1; j < i + len; ++j) {
                    if (!greatest || greatest->value() < great[i][j]->value() / small[j][i + len]->value()) {
                        greatest = new Pair(great[i][j], small[j][i + len]);
                    }
                    if (!smallest || smallest->value() > small[i][j]->value() / great[j][i + len]->value()) {
                        smallest = new Pair(small[i][j], great[j][i + len]);
                    }
                }
                great[i][i + len] = greatest;
                small[i][i + len] = smallest;
            }
        }
        return great[0][nums.size()]->toString();
    }
};

Solution 2. Math

To maximize a, b, c we pick the greater one from a / b / c and a / (b / c).

Let’s compare them.

Since a / b / c = a / (b * c) and a / (b / c) = a * c / b.

Multiplying b / a we get 1 / c and c. So given c >= 1 we always have 1 / c <= c, and thus a / b / c <= a / (b / c).

So given a, b, c, a / (b / c) is always greater than a / b / c.

For a, b, c, d:

  • If we divide it into a and b, c, d, the greatest result is a / (b / c / d) = a * c * d / b.
  • If we divide it into a, b and c, d, the result is (a / b) / (c / d) = a * d / (b * c).
  • If we devide it into a, b, c and d, the greatest result is a / (b / c) / d = a * c / (b * d).

We can see a * c * d / b is the greatest since its denominator is the smallest.

So given a, b, c, d, a / (b / c / d) is the greatest division.

Generally, the best division of x_1, x_2, ..., x_n is x_1 / (x_2 / ... / x_n) (which equals x_1 * x_3 * ... x_n / x_2).

// OJ: https://leetcode.com/problems/optimal-division/

// Time: O(N)
// Space: O(1)
class Solution {
public:
    string optimalDivision(vector<int>& nums) {
        if (nums.size() == 1) return to_string(nums[0]);
        if (nums.size() == 2) return to_string(nums[0]) + "/" + to_string(nums[1]);
        string ans = to_string(nums[0]) + "/(" + to_string(nums[1]);
        for (int i = 2; i < nums.size(); ++i) ans += "/" + to_string(nums[i]);
        return ans += ")";
    }
};

Java

class Solution {
    public String optimalDivision(int[] nums) {
        if (nums == null || nums.length == 0)
            return "";
        int length = nums.length;
        if (length == 1)
            return String.valueOf(nums[0]);
        if (length == 2)
            return nums[0] + "/" + nums[1];
        StringBuffer sb = new StringBuffer();
        sb.append(String.valueOf(nums[0]));
        sb.append("/");
        sb.append("(");
        for (int i = 1; i < length; i++) {
            sb.append(String.valueOf(nums[i]));
            if (i < length - 1)
                sb.append("/");
        }
        sb.append(")");
        return sb.toString();
    }
}

All Problems

All Solutions