Welcome to Subscribe On Youtube
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:
- 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.
Companies:
Amazon
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
andb, c, d
, the greatest result isa / (b / c / d) = a * c * d / b
. - If we divide it into
a, b
andc, d
, the result is(a / b) / (c / d) = a * d / (b * c)
. - If we devide it into
a, b, c
andd
, the greatest result isa / (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 += ")";
}
};
-
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(); } } ############ class Solution { public String optimalDivision(int[] nums) { int n = nums.length; if (n == 1) { return nums[0] + ""; } if (n == 2) { return nums[0] + "/" + nums[1]; } StringBuilder ans = new StringBuilder(nums[0] + "/("); for (int i = 1; i < n - 1; ++i) { ans.append(nums[i] + "/"); } ans.append(nums[n - 1] + ")"); return ans.toString(); } }
-
// 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(); } };
-
class Solution: def optimalDivision(self, nums: List[int]) -> str: n = len(nums) if n == 1: return str(nums[0]) if n == 2: return f'{nums[0]}/{nums[1]}' return f'{nums[0]}/({"/".join(map(str, nums[1:]))})' ############ class Solution(object): def optimalDivision(self, nums): """ :type nums: List[int] :rtype: str """ if len(nums) < 3: return "/".join(map(str, nums)) return "%s/(%s)" % (nums[0], "/".join(map(str, nums[1:])))
-
func optimalDivision(nums []int) string { n := len(nums) if n == 1 { return strconv.Itoa(nums[0]) } if n == 2 { return fmt.Sprintf("%d/%d", nums[0], nums[1]) } ans := &strings.Builder{} ans.WriteString(fmt.Sprintf("%d/(", nums[0])) for _, num := range nums[1 : n-1] { ans.WriteString(strconv.Itoa(num)) ans.WriteByte('/') } ans.WriteString(fmt.Sprintf("%d)", nums[n-1])) return ans.String() }
-
function optimalDivision(nums: number[]): string { const n = nums.length; const res = nums.join('/'); if (n > 2) { const index = res.indexOf('/') + 1; return `${res.slice(0, index)}(${res.slice(index)})`; } return res; }
-
impl Solution { pub fn optimal_division(nums: Vec<i32>) -> String { let n = nums.len(); match n { 1 => nums[0].to_string(), 2 => nums[0].to_string() + "/" + &nums[1].to_string(), _ => { let mut res = nums[0].to_string(); res.push_str("/("); for i in 1..n { res.push_str(&nums[i].to_string()); res.push('/'); } res.pop(); res.push(')'); res } } } }