• [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

  • 相关阅读:
    CSU 1333 Funny Car Racing
    FZU 2195 检查站点
    FZU 2193 So Hard
    ZOJ 1655 FZU 1125 Transport Goods
    zoj 2750 Idiomatic Phrases Game
    hdu 1874 畅通工程续
    hdu 2489 Minimal Ratio Tree
    hdu 3398 String
    洛谷 P2158 [SDOI2008]仪仗队 解题报告
    POJ 1958 Strange Towers of Hanoi 解题报告
  • 原文地址:https://www.cnblogs.com/lz87/p/14324972.html
Copyright © 2020-2023  润新知