https://leetcode.com/problems/optimal-division
X1/X2/X3/../Xn will always be equal to (X1/X2) * Y, no matter how you place parentheses. i.e no matter how you place parentheses, X1 always goes to the numerator and X2 always goes to the denominator. Hence you just need to maximize Y. And Y is maximized when it is equal to X3 *..*Xn. So the answer is always X1/(X2/X3/../Xn) = (X1 *X3 *..*Xn)/X2
https://discuss.leetcode.com/topic/86475/simple-java-solution
https://leetcode.com/articles/optimal-division/
Approach #2 Using Memorization [Accepted]
https://leetcode.com/problems/optimal-division/discuss/101697/Java-Solution-Backtracking
https://leetcode.com/problems/optimal-division/discuss/101684/Brute-force-with-memory-in-case-of-your-interviewer-forbid-tricky-solution
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:
- The length of the input array is [1, 10].
- Elements in the given array will be in range [2, 1000].
- There is only one optimal division for each test case.
X1/X2/X3/../Xn will always be equal to (X1/X2) * Y, no matter how you place parentheses. i.e no matter how you place parentheses, X1 always goes to the numerator and X2 always goes to the denominator. Hence you just need to maximize Y. And Y is maximized when it is equal to X3 *..*Xn. So the answer is always X1/(X2/X3/../Xn) = (X1 *X3 *..*Xn)/X2
public String optimalDivision(int[] nums) {
StringBuilder builder = new StringBuilder();
builder.append(nums[0]);
for (int i = 1; i < nums.length; i++) {
if (i == 1 && nums.length > 2) {
builder.append("/(").append(nums[i]);
} else {
builder.append("/").append(nums[i]);
}
}
return nums.length > 2 ? builder.append(")").toString() : builder.toString();
}
public String optimalDivision(int[] nums) {
if (nums.length == 1)
return nums[0] + "";
if (nums.length == 2)
return nums[0] + "/" + nums[1];
String res = nums[0] + "/(" + nums[1];
for (int i = 2; i < nums.length; i++) {
res += "/" + nums[i];
}
return res + ")";
}
X. When the num may be less than 1 such as 0.1 etchttps://leetcode.com/articles/optimal-division/
Brute force of this problem is to divide the list into two parts and and call function for these two parts. We will iterate from to so that and .
and parts return their maximum and minimum value and corresponding strings.
Minimum value can be found by dividing minimum of left by maximum of right i.e. .
Similarly,Maximum value can be found by dividing maximum of left value by minimum of right value. i.e. .
Now, how to add parenthesis? As associativity of division operator is from left to right i.e. by default left most divide should be done first, we need not have to add paranthesis to the left part, but we must add parenthesis to the right part.
eg- "2/(3/4)" will be formed as leftPart+"/"+"("+rightPart+")", assuming leftPart is "2" and rightPart is"3/4".
One more point, we also don't require parenthesis to right part when it contains single digit.
eg- "2/3", here left part is "2" and right part is "3" (contains single digit) . 2/(3) is not valid.
public String optimalDivision(int[] nums) {
Result t = optimal(nums, 0, nums.length - 1, "");
return t.max_str;
}
class Result {
float max_val, min_val;
String min_str, max_str;
}
public Result optimal(int[] nums, int start, int end, String res) {
Result t = new Result();
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++) {
Result left = optimal(nums, start, i, "");
Result 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;
}
Approach #2 Using Memorization [Accepted]
In the above approach we called optimal function recursively for ever and . We can notice that there are many redundant calls in the above approach, we can reduce these calls by using memorization to store the result of different function calls. Here, array is used for this purpose.
- Time complexity : . array of size is filled and filling of each cell of the array takes time.
- Space complexity : . array of size where each cell of array contains string of length .
class Result {
float max_val, min_val;
String min_str, max_str;
}
public String optimalDivision(int[] nums) {
Result[][] memo = new Result[nums.length][nums.length];
Result t = optimal(nums, 0, nums.length - 1, "", memo);
return t.max_str;
}
public Result optimal(int[] nums, int start, int end, String res, Result[][] memo) {
if (memo[start][end] != null)
return memo[start][end];
Result t = new Result();
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++) {
Result left = optimal(nums, start, i, "", memo);
Result 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;
}
https://leetcode.com/problems/optimal-division/discuss/101697/Java-Solution-Backtracking
https://leetcode.com/problems/optimal-division/discuss/101684/Brute-force-with-memory-in-case-of-your-interviewer-forbid-tricky-solution
class Result {
String str;
double val;
}
public String optimalDivision(int[] nums) {
int len = nums.length;
return getMax(nums, 0, len - 1).str;
}
private Result getMax(int[] nums, int start, int end) {
Result r = new Result();
r.val = -1.0;
if (start == end) {
r.str = nums[start] + "";
r.val = (double)nums[start];
}
else if (start + 1 == end) {
r.str = nums[start] + "/" + nums[end];
r.val = (double)nums[start] / (double)nums[end];
}
else {
for (int i = start; i < end; i++) {
Result r1 = getMax(nums, start, i);
Result r2 = getMin(nums, i + 1, end);
if (r1.val / r2.val > r.val) {
r.str = r1.str + "/" + (end - i >= 2 ? "(" + r2.str + ")" : r2.str);
r.val = r1.val / r2.val;
}
}
}
//System.out.println("getMax " + start + " " + end + "->" + r.str + ":" + r.val);
return r;
}
private Result getMin(int[] nums, int start, int end) {
Result r = new Result();
r.val = Double.MAX_VALUE;
if (start == end) {
r.str = nums[start] + "";
r.val = (double)nums[start];
}
else if (start + 1 == end) {
r.str = nums[start] + "/" + nums[end];
r.val = (double)nums[start] / (double)nums[end];
}
else {
for (int i = start; i < end; i++) {
Result r1 = getMin(nums, start, i);
Result r2 = getMax(nums, i + 1, end);
if (r1.val / r2.val < r.val) {
r.str = r1.str + "/" + (end - i >= 2 ? "(" + r2.str + ")" : r2.str);
r.val = r1.val / r2.val;
}
}
}
//System.out.println("getMin " + start + " " + end + "->" + r.str + ":" + r.val);
return r;
}