zoukankan      html  css  js  c++  java
  • 数组问题常用的O(N)算法:单调队列

    求max(a)<min(b)的区间个数

    给定两个长度都为N的整型数组a[N]和b[N],求满足如下条件的闭区间个数:在区间[l,r]上,a中的任意元素都比b中的任意元素小。

    这个问题是O(N)复杂度。
    两根指针l和r一前一后向后走,对于每个l,寻找最靠右的满足max(a[l:r])<min(b[l:r])的r值。r-l+1就表示如果以l为左区间,合法的r的个数。累加此值即可。关键在于:r的运动是单向的。
    因为:如果在大区间[l,r]上满足max(a)<min(b),那么在[l+1,r]自区间上必然也满足max(a)<min(b),所以r是单向运动的。使用两个单调队列可以实现O(N)复杂度。

    import java.util.LinkedList;
    import java.util.Scanner;
    
    public class Main {
    void pushMax(LinkedList<Integer> q, int value) {
        while (!q.isEmpty() && q.getLast() < value) q.removeLast();
        q.add(value);
    }
    
    void pushMin(LinkedList<Integer> q, int value) {
        while (!q.isEmpty() && q.getLast() > value) q.removeLast();
        q.add(value);
    }
    
    boolean ok(LinkedList<Integer> max, LinkedList<Integer> min) {
        if (max.isEmpty() || min.isEmpty()) return true;
        if (max.getFirst() < min.getFirst()) return true;
        return false;
    }
    
    void deq(LinkedList<Integer> q, int value) {
        if (!q.isEmpty() && value == q.getFirst()) q.removeFirst();
    }
    
    Main() {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int[] a = new int[n];
        int[] b = new int[n];
        for (int i = 0; i < n; i++) a[i] = cin.nextInt();
        for (int i = 0; i < n; i++) b[i] = cin.nextInt();
        int s = 0;
        int r = 0;
        LinkedList<Integer> maxQ = new LinkedList<>(), minQ = new LinkedList<>();
        int lastR = -1;
        for (int i = 0; i < n; i++) {
            for (r = Math.max(r, i); r < n; r++) {
                if (lastR != r) {
                    pushMax(maxQ, a[r]);
                    pushMin(minQ, b[r]);
                    lastR = r;
                }
    //            System.out.println(maxQ + " " + minQ);
                if (!ok(maxQ, minQ)) break;
            }
            s += r - i;
    
            deq(maxQ, a[i]);
            deq(minQ, b[i]);
        }
        System.out.println(s);
    }
    
    public static void main(String[] args) {
        new Main();
    }
    }
    

    求全部区间极值之和

    给定数组a[N],可以确定(N+1)*N/2个区间,每个区间都有极大值、极小值,求所有区间的极大值、极小值之差。

    问题可以转化为:求数字a[i]充当了几次极大值、充当了几次极小值,最终 极差之和$=sum{a_i imes (maxCount-minCount)} $
    关键在于如何求a[i]当了几次极大值、当了几次极小值。只需要知道从a[i]向左望去看到的第一个比a[i]大的值得坐标leftMax,向右望去看到的第一个比a[i]大的坐标rightMax,那么a[i]充当极大值的次数为$(i-leftMax) imes (rightMax-i)$

    使用单调栈可以O(N)复杂度求出全部元素的左望最大和右望最大。

    import java.util.Scanner;
    import java.util.Stack;
    
    public class Main {
    
    Main() {
        Scanner cin = new Scanner(System.in);
        int n = cin.nextInt();
        int[] a = new int[n];
        for (int i = 0; i < n; i++) a[i] = cin.nextInt();
        int[] maxLeft = new int[n], maxRight = new int[n], minLeft = new int[n], minRight = new int[n];
        Stack<Integer> maxStack = new Stack<>();
        Stack<Integer> minStack = new Stack<>();
        for (int i = 0; i < n; i++) {
            while (!maxStack.isEmpty()) {
                int index = maxStack.peek();
                if (a[index] <= a[i]) {//此处等号是关键
                    maxStack.pop();
                    maxRight[index] = i;//index最右边能到达的位置
                } else {
                    break;
                }
            }
            int leftIndex = 0;
            if (!maxStack.isEmpty()) leftIndex = maxStack.peek() + 1;
            maxLeft[i] = leftIndex;
            maxStack.push(i);
    
            while (!minStack.isEmpty()) {
                int index = minStack.peek();
                if (a[index] >= a[i]) {
                    minStack.pop();
                    minRight[index] = i;//index最左边能到达的位置
                } else {
                    break;
                }
            }
            int rightIndex = 0;
            if (!minStack.isEmpty()) rightIndex = minStack.peek() + 1;
            minLeft[i] = rightIndex;
            minStack.push(i);
        }
        while (!maxStack.isEmpty()) {
            maxRight[maxStack.pop()] = n;
        }
        while (!minStack.isEmpty()) {
            minRight[minStack.pop()] = n;
        }
        int s = 0;
        for (int i = 0; i < n; i++) {
            int maxCount = (i - maxLeft[i] + 1) * (maxRight[i] - i), minCount = (i - minLeft[i] + 1) * (minRight[i] - i);
            s += a[i] * (maxCount - minCount);
        }
        System.out.println(s);
    }
    
    public static void main(String[] args) {
        new Main();
    }
    }
    
  • 相关阅读:
    微软Enterprise Library 4.0将支持依赖注入
    javascript cookies 存、取、删除实例
    动态调用 WebService(转)
    IE缓存是什么?cookies是什么?
    序列化
    PKI
    ASP.NET的(HttpModule,HttpHandler)(转)
    PKI基础 二.PKI基础5.数字证书及应用(转,一个加密解密的全过程)
    AOP技术基础(转)
    getChildByName()与getChildAt()效率比较
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/9499427.html
Copyright © 2011-2022 走看看