# [AtCoder] Mandarin Orange

Problem Link: C - Mandarin Orange

The problem can be converted to as such: Given an array A of positive integers, find out the the maximum product value Len * Min{L, R}. Len is the length of subarray A[L, R] and Min{L, R} is the minimum value in this subarray.

Solution 1. O(N^3) brute force

Obviously we can just check all O(N^2) subarrays, each check takes O(N) time to find a subarray min value.

Solution 2. O(N^2 * logN), optimizing min value check using segment tree

    static class SegmentTree {
int leftMost, rightMost;
SegmentTree lChild, rChild;
int min;

SegmentTree(int leftMost, int rightMost, int[] a) {
this.leftMost = leftMost;
this.rightMost = rightMost;
if(leftMost == rightMost) {
min = a[leftMost];
}
else {
int mid = leftMost + (rightMost - leftMost) / 2;
lChild = new SegmentTree(leftMost, mid, a);
rChild = new SegmentTree(mid + 1, rightMost, a);
recalc();
}
}

void recalc() {
if(leftMost == rightMost) return;
min = Math.min(lChild.min, rChild.min);
}

void pointUpdate(int index, int newVal) {
if(leftMost == rightMost) {
min = newVal;
return;
}
if(index <= lChild.rightMost) lChild.pointUpdate(index, newVal);
else rChild.pointUpdate(index, newVal);
recalc();
}

int minQuery(int l, int r) {
if(l > rightMost || r < leftMost) return Integer.MAX_VALUE;
if(l <= leftMost && r >= rightMost) return min;
return Math.min(lChild.minQuery(l, r), rChild.minQuery(l, r));
}
}

static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt();
int[] a = in.nextIntArrayPrimitive(n);
SegmentTree st = new SegmentTree(0, n - 1, a);
int ans = 0;
for(int l = 0; l < n; l++) {
for(int r = l; r < n; r++) {
ans = Math.max(ans, st.minQuery(l, r) * (r - l + 1));
}
}
out.println(ans);
}
out.close();
}

Solution 3. O(N^2), optimizing min value check by pre-computing the range min query sparse table in O(N * logN) time and space.

    static class RangeMinQuery_SparseTable {
int n, k;
int[] log;
int[][] rangeMin;

public RangeMinQuery_SparseTable(int[] a) {
n = a.length;
log = new int[n + 1];   //log[i]: 2^log[i] = i; for i that is not 2^j, log[i] rounds down to the closest such 2^j that 2^j < i
log[1] = 0; //2^0 = 1
//precompute log
for (int i = 2; i <= n; i++) {
log[i] = log[i / 2] + 1;
}
k = log[n]; //if n is not 2^j, k is rounded down, so when initializing rangeMin we need to use k + 1
rangeMin = new int[n][k + 1];
for(int i = 0; i < n; i++) {
rangeMin[i][0] = a[i];
}
//rangeMin[i][j] is the min in range[i, i + 2^j - 1] of length 2^j
for(int j = 1; j <= k; j++) {
for(int i = 0; i + (1 << j) <= n; i++) {
rangeMin[i][j] = Math.min(rangeMin[i][j - 1], rangeMin[i + (1 << (j - 1))][j - 1]);
}
}
}

public int query(int L, int R) {
//2^j is at least half the size of range [L, R], at most the entire size of range[L, R]
int j = log[R - L + 1];
return Math.min(rangeMin[L][j], rangeMin[R - (1 << j) + 1][j]);
}
}
static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt();
int[] a = in.nextIntArrayPrimitive(n);
RangeMinQuery_SparseTable rmst = new RangeMinQuery_SparseTable(a);
int ans = 0;
for(int l = 0; l < n; l++) {
for(int r = l; r < n; r++) {
ans = Math.max(ans, rmst.query(l, r) * (r - l + 1));
}
}
out.println(ans);
}
out.close();
}

Solution 4. O(N^2), fixing L, then update min value while increasing R one by one. (Keep a running min)

    static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt();
int[] a = in.nextIntArrayPrimitive(n);
int ans = 0;
for(int l = 0; l < n; l++) {
int currMin = a[l];
for(int r = l; r < n; r++) {
currMin = Math.min(currMin, a[r]);
ans = Math.max(ans, currMin * (r - l + 1));
}
}
out.println(ans);
}
out.close();
}

Solution 5. O((maxV + N) * logN).

Using the fact that max of A's element is only up to O(10^5), we can iterate over all min value candidates and find the max answer. This is done as following.

1.   Create a tree mapping from A[i] to all of its indices in A, call it tm.

2.  Create a tree set that stores all the smaller values' indices, call it prevIdx.

3.  From 1 to max possible value in A, do the following:

(a). if the current value exists in A, iterate over all of its indices and for each index i find the largest index l such that l < i and the smallest index r such that r > i. l and r are the left and right boundaries of the the current value at index i being the min value.

(b). after (a), add of the indices of the current value to prevIdx for bigger value checks.

The runtime is O((maxV + N) * logN). We need to iterare from 1 to maxV. And there are N total indices to all to prevIdx. For each of these N indices, we do 2 logN operations to get the left and right boundaries. Lastly, the tree map has at most N entries, we do maxV existence check, each costs O(logN) time.

    static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt();
int[] a = in.nextIntArrayPrimitive(n);
int maxV = 0;
for(int v : a) {
maxV = Math.max(maxV, v);
}
TreeMap<Integer, List<Integer>> tm = new TreeMap<>();
for(int i = 0; i < n; i++) {
}
TreeSet<Integer> prevIdx = new TreeSet<>();
int ans = 0;
for(int x = 1; x <= maxV; x++) {
if(tm.containsKey(x)) {
List<Integer> idx = tm.get(x);
for(int i : idx) {
Integer l = prevIdx.lower(i);
Integer r = prevIdx.higher(i);
if(l == null) {
l = -1;
}
if(r == null) {
r = n;
}
ans = Math.max(ans, (r - l - 1) * x);
}
}
}
out.println(ans);
}
out.close();
}

Solution 6. O(N), this problem is the same with finding the largest rectangle in histogram. Treat each A[i] as a histogram height at index i.

Keep a non-decreasing stack, each time the current a[i] is smaller than the top of the stack j, we just find the right bound of the rectangle of height h[j]. Its left bound is just beneath itself in the stack. After iterating over all a[i] and if there are still unprocessed heights in the stack, repeat the same popping process using N as their right bound.

    static void solve(int testCnt) {
for (int testNumber = 0; testNumber < testCnt; testNumber++) {
int n = in.nextInt();
int[] a = in.nextIntArrayPrimitive(n);
ArrayDeque<Integer> q = new ArrayDeque<>();
int ans = 0;
for(int i = 0; i < n; i++) {
while(q.peekFirst() >= 0 && a[q.peekFirst()] > a[i]) {
int j = q.removeFirst();
ans = Math.max(ans, (i - q.peekFirst() - 1) * a[j]);
}
}
while(q.peekFirst() >= 0) {
int idx = q.removeFirst();
ans = Math.max(ans, (n - q.peekFirst() - 1) * a[idx]);
}
out.println(ans);
}
out.close();
}

Related Problems:

[LeetCode 84] Largest Rectangle in Histogram

posted @ 2021-02-16 01:08  Review->Improve  阅读(11)  评论(0编辑  收藏