zoukankan      html  css  js  c++  java
  • RMQ (Range Minimal Query) 问题 ,稀疏表 ST

    RMQ ( 范围最小值查询 ) 问题是一种动态查询问题,它不需要修改元素,但要及时回答出数组 A 在区间 [l, r] 中最小的元素值

    RMQ(Range Minimum/Maximum Query):对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。

    对于 RMQ ,我们通常关心两方面的算法效率:预处理时间和查询时间。
    解决一般 RMQ 问题的三种方法
    胜者树 (Winner Tree) O(n)-O(logn)
    稀疏表 (Sparse Table) O(nlogn)-O(1)
    线段树 (Segment Tree) O(n)-O(logn)
    这里介绍一个稀疏表算法。

    一、稀疏表ST算法--预处理

    二、稀疏表ST算法--查询

    三、RMQ ST模板

    package ads;
    
    public class RMQ_ST {
        
        int[] a;
        int[][] st;//st[i][j],i表示数组a的索引为i的元素,即i:索引
        int n;
        public RMQ_ST(int[] a) {
            this.a = a;
            this.n = a.length;
            st = new int[n][17];
        }
        
        void initRMQ() {
            int k = (int)(Math.log((double)n) / Math.log(2.0));
            for (int i=0; i<n; i++) st[i][0] = i;
            for (int j=1; j<=k; j++) {    //dp开始,从1到k
                for (int i=0; i<n; i++) {    //遍历每个元素
                    st[i][j] = st[i][j-1];    //先赋值为前一半的RMQ值
                    int part = i + (1 << (j-1));//后一半的第一个元素索引
                    if (part >= n) break;    //如果后一半的第一个元素已经超出数组范围,直接跳过后面的元素
                    if (a[ st[i][j-1] ] > a[ st[part][j-1] ]) st[i][j] = st[part][j-1];
                }
            }
        }
        
        int rmq(int x, int y) {
            int k = (int)(Math.log((double)y-x+1) / Math.log(2.0));
            int part = y - (1<<k) + 1;//切分,比如0~4切分成st(0,2)和st(1,2),前者是0~3的最小元素,后者是1~4的
            if (a[ st[x][k] ] < a[ st[part][k] ]) return st[x][k];
            return st[part][k];
        }
        
        public static void main(String[] args) {
            int[] numbers = {4,3,2,1,6,7,8,9};
            RMQ_ST st = new RMQ_ST(numbers);
            st.initRMQ();
            System.out.println(st.rmq(0, 3));
            System.out.println(st.rmq(4, 7));
        }
    
    }

    四、示例代码

    poj2425 http://poj.org/problem?id=2452

    思路:枚举每个位置 i ,找出右边第一个比 a[i] 小的元素位置j

      在 i 到 j-1 中间求最大值的位置 k ,如果 a[k] > a[i],那么更新答案

    Description

    Xuanxuan has n sticks of different length. One day, she puts all her sticks in a line, represented by S1, S2, S3, ...Sn. After measuring the length of each stick Sk (1 <= k <= n), she finds that for some sticks Si and Sj (1<= i < j <= n), each stick placed between Si and Sj is longer than Si but shorter than Sj. 

    Now given the length of S1, S2, S3, …Sn, you are required to find the maximum value j - i.

    Input

    The input contains multiple test cases. Each case contains two lines. 
    Line 1: a single integer n (n <= 50000), indicating the number of sticks. 
    Line 2: n different positive integers (not larger than 100000), indicating the length of each stick in order.

    Output

    Output the maximum value j - i in a single line. If there is no such i and j, just output -1.

    Sample Input

    4
    5 4 3 6
    4
    6 5 4 3
    

    Sample Output

    1
    -1
    

    Source

     
    #include <iostream>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    
    const int MAXN = 50005;
    
    int n;
    int a[MAXN];
    int st1[MAXN][17],  st2[MAXN][17];//分别是最小RMQ和最大RMQ
    int d[32];
    
    void rmqinit() {
        for (int i=0; i<=n; i++) st1[i][0] = st2[i][0] = i;
        int k = int(log(double(n)) / log(2.0)) + 1;
        for (int j=1; j<k; j++) {
            for (int i=0; i<n; i++) {
                st1[i][j] = st1[i][j-1];
                st2[i][j] = st2[i][j-1];
                int part = i + (1 << (j-1));
                if (part >= n) break;
                if (a[ st1[i][j-1] ] > a[ st1[part][j-1] ]) st1[i][j] = st1[part][j-1];
                if (a[ st2[i][j-1] ] < a[ st2[part][j-1] ]) st2[i][j] = st2[part][j-1];
            }
        }
    }
    
    int rmqmin(int l, int r) {
        int k = int( log(double(r-l+1)) / log(2.0) );
        int part = r - (1<<k) + 1;
        if (a[ st1[l][k] ] < a[ st1[part][k] ]) return st1[l][k];
        return st1[part][k];
    }
    
    int rmqmax(int l, int r) {
        int k = int( log(double(r-l+1)) / log(2.0) );
        int part = r - (1<<k) + 1;
        if (a[ st2[l][k] ] > a[ st2[part][k] ]) return st2[l][k];
        return st2[part][k];
    }
    
    int bin_search(int a) {
        int l = a, r = n - 1;
        while (l < r) {
            int mid = ((l+r)>>1) + ((l+r)&1);
            if (a == rmqmin(a, mid)) { //如果l~mid范围内的最小元素是l,说明l~mid都大于元素l
                l = mid;
            }
            else {
                r = mid - 1;
            }
        }
        return l;
    }
    
    int work() {
        int res = -1;
        for (int i=0; i<n; i++) {
            int r = rmqmax(i, bin_search(i));
            res = max(res, r-i);
        }
        if (res == 0) return -1;
        return res;
    }
    
    int main()
    {
        while (scanf("%d", &n) != EOF) {
            for (int i=0; i<n; i++) {
                scanf("%d", a+i);
            }
    
            rmqinit();
            printf("%d
    ", work());
        }
    
        //cout << "Hello world!" << endl;
        return 0;
    }
  • 相关阅读:
    HttpServlet RequestDispatcher sendredirect和forward
    java中判断字符串是否为数字的方法的几种方法
    apk 解包 打包
    js cookie
    js中的this关键字
    Myeclipse 添加Android开发工具
    List<T> List<?> 区别用法
    mysql 命令
    BZOJ 2281 Luogu P2490 [SDOI2011]黑白棋 (博弈论、DP计数)
    【学习笔记】求矩阵的特征多项式
  • 原文地址:https://www.cnblogs.com/549294286/p/3780794.html
Copyright © 2011-2022 走看看