题目

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.


解法

1. 暴力破解

用i从数组的start到end进行遍历,每个i都能将数组分为两部分,left=(start, i),right=(i+1,end)

按照题目要求,需要求该数组(start,end)的最大值,所以如果能将left取到最大值,right取到最小值,则能left/right得到最大值

所以问题又细分到两个小数组(start, i)的最大值(i+1,end)的最小值,用同样的方法递归将两个数组再细分成left跟right就可以求出,其中要求最大值就max(left)/min(right),求最小值就min(left)/max(right)

至于加括号,很显然,如果right数组超过一个数字就需要加上括号,反之则不用,left因为本来就优先运算,不用加括号

另外注意既然是除法,有可能产生小数,需要额外处理

public class Solution {
    public String optimalDivision(int[] nums) {
        T t = optimal(nums, 0, nums.length - 1, "");
        return t.max_str;
    }
    class T {
        float max_val, min_val;
        String min_str, max_str;
    }
    public T optimal(int[] nums, int start, int end, String res) {
        T t = new T();
        if (start == end) {
            t.max_val = nums[start];
            t.min_val = nums[start];
            t.min_str = "" + nums[start];
            t.max_str = "" + nums[start];
            return t;
        }
        t.min_val = Float.MAX_VALUE;
        t.max_val = Float.MIN_VALUE;
        t.min_str = t.max_str = "";
        for (int i = start; i < end; i++) {
            T left = optimal(nums, start, i, "");
            T right = optimal(nums, i + 1, end, "");
            if (t.min_val > left.min_val / right.max_val) {
                t.min_val = left.min_val / right.max_val;
                t.min_str = left.min_str + "/" + (i + 1 != end ? "(" : "") + right.max_str + (i + 1 != end ? ")" : "");
            }
            if (t.max_val < left.max_val / right.min_val) {
                t.max_val = left.max_val / right.min_val;
                t.max_str = left.max_str + "/" + (i + 1 != end ? "(" : "") + right.min_str + (i + 1 != end ? ")" : "");
            }
        }
        return t;
    }
}

这种解法时间复杂度是O(n!),空间复杂度是O(n2)

显然其中存在很多重复的运算,立刻想到用空间换时间


2.空间换时间,用二维数组记录已经计算过的结果

这个二维数组memo[start][end]记录题中数组(start, end)的最大最小值

public class Solution {
    class T {
        float max_val, min_val;
        String min_str, max_str;
    }
    public String optimalDivision(int[] nums) {
        T[][] memo = new T[nums.length][nums.length];
        T t = optimal(nums, 0, nums.length - 1, "", memo);
        return t.max_str;
    }
    public T optimal(int[] nums, int start, int end, String res, T[][] memo) {
        if (memo[start][end] != null)
            return memo[start][end];
        T t = new T();
        if (start == end) {
            t.max_val = nums[start];
            t.min_val = nums[start];
            t.min_str = "" + nums[start];
            t.max_str = "" + nums[start];
            memo[start][end] = t;
            return t;
        }
        t.min_val = Float.MAX_VALUE;
        t.max_val = Float.MIN_VALUE;
        t.min_str = t.max_str = "";
        for (int i = start; i < end; i++) {
            T left = optimal(nums, start, i, "", memo);
            T right = optimal(nums, i + 1, end, "", memo);
            if (t.min_val > left.min_val / right.max_val) {
                t.min_val = left.min_val / right.max_val;
                t.min_str = left.min_str + "/" + (i + 1 != end ? "(" : "") + right.max_str + (i + 1 != end ? ")" : "");
            }
            if (t.max_val < left.max_val / right.min_val) {
                t.max_val = left.max_val / right.min_val;
                t.max_str = left.max_str + "/" + (i + 1 != end ? "(" : "") + right.min_str + (i + 1 != end ? ")" : "");
            }
        }
        memo[start][end] = t;
        return t;
    }
}

但就算这样子时间复杂度还是有O(n3),空间复杂度升到O(n3)


3.使用数学定理

仔细分析发现,用些数学的手段可以很轻松得到解答,不需要遍历整个数组

对于数组[a, b, c, d],若要加优先级使a/b/c/d最大,则b/c/d需要最小

其实加上括号可以改变优先级,去掉括号就相当于把括号里的/变成*,*变成/

所以除了第一个/没法改变,其他后面的/都可以通过加括号使其变为*

显然需要得到最大值,就需要最多的*,那么a/(b/c/d/...)=a*c*d*.../b显示是我们要求的最大值

public class Solution {
    public String optimalDivision(int[] nums) {
        if (nums.length == 1)
            return nums[0] + "";
        if (nums.length == 2)
            return nums[0] + "/" + nums[1];
        StringBuilder res = new StringBuilder(nums[0] + "/(" + nums[1]);
        for (int i = 2; i < nums.length; i++) {
            res.append("/" + nums[i]);
        }
        res.append(")");
        return res.toString();
    }
}

这样的时间复杂度是线性的O(n),空间复杂度也是O(n)