[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++) {
                tm.computeIfAbsent(a[i], k -> new ArrayList<>()).add(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);
                    }
                    prevIdx.addAll(idx);
                }
            }
            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<>();
            q.addFirst(-1);
            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]);
                }
                q.addFirst(i);
            }
            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编辑  收藏