zoukankan      html  css  js  c++  java
  • RMQ区间最值查询

    RMQ区间最值查询

    概述

    RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,

    回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。这两个问题是在实际应用中经

    常遇到的问题,下面介绍一下解决这两种问题的比较高效的算法。

    分析:

    对于该问题,最容易想到的解决方案是遍历,复杂度是O(n)。但当数据量非常大且查询很频繁时,该算法无法

    在有效的时间内查询出正解。

    这里介绍Tarjan的Sparse-Table算法,预处理时间为O(nlogn),但查询只需要O(1),并且常数很小,算法也很容易写出。

    预处理

    设A[i]是要求区间最值的数列,F[i, j]  表示从第  i 个数起连续  2  个数中的最大值。(DP的状态)

    例如 A 数列为:3 2 4 5 6 8 1 2 9 7

    F[1,0]表示第1个数起,长度为20=1的最大值,其实就是3这个数。

    同理 F[1,1] = max(3,2) = 3, F[1,2]=max(3,2,4,5) = 5,F[1,3] = max(3,2,4,5,6,8,1,2) = 8;

    首先:F[i,0]就等于A[i]。(DP的初始值)

    其次:我们把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从 i 到i + 2 (j - 1) - 1为一段,i + 2  (j - 1)  到 i + 2 j - 1为一段

    于是我们得到了

    状态转移方程             F[i, j]=max(F[i,j-1], F[i + 2(j-1),j-1])

    void RMQ_init(const vector<int> &A) {
        int n = A.size();
        for(int i = 0; i < n; ++i) d[i][0] = A[i];
        for(int j = 1; (1 << j) <= n; ++j)
            for(int i = 0; i + (1 << j) - 1 < n; ++i)
                d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]);
    }

     查询:

    假如我们需要查询的区间为(i,j),那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂

    因为这个区间的长度为  j - i + 1,所以我们可以取   k=log2( j - i + 1) ,则有:RMQ(A, i, j)=max{   F[i , k] ,   F[ j - 2 k + 1, k]   }
    举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 2 + 1, 2]) = max(F[2, 2],F[5, 2]);

     int RMQ(int L, int R) {
         int k = 0;
        while((1 << (k + 1)) <= R - L + 1) ++k;
        return max(d[L][k], d[R - (1 << k) + 1][k]);
     }

    例题:

    题目描述

    老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,
    由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。
    于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,
    问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

    输入格式:

    输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。
    第二行为m个数,分别是账目的钱数
    后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

    输出格式:

    输出文件中为每个问题的答案。具体查看样例。

    输入输出样例

    输入样例#1:
    10 3
    1 2 3 4 5 6 7 8 9 10
    2 7
    3 9
    1 10
    输出样例#1:

    2 3 1

     代码:

    #include<iostream>
    #include<bits/stdc++.h>
    using namespace std;
    
    int dp[500100][20];//dp[i][j]是从第i个数起,到第2^j个数止的,最小值
    int main(){
        int n,q;
        cin>>n>>q;
        int *arr = new int[n+1];
        for (int i = 1; i <= n; i++){
            cin>>arr[i];
        }
    
        for (int i = 1; i <= n; i++)  dp[i][0]=arr[i];
        int count=0;
        while((1<<count)<=n)count++;
        // cout<<"count="<<count<<"  "<<endl;
        for(int j=1;j<=count;j++)
            for(int i=1;i<=n;i++)
                dp[i][j]=min(dp[i][j-1],dp[i+ (1<< (j-1) ) ][j-1]);
        int L,R,k;
        while (q--){
            cin>>L>>R;
            k=0;
            while (1<<(k+1)<=R-L+1)k++;
            cout<<min(dp[L][k],dp[R-(1<<k)+1][k])<<" ";
        }
    }

     

    因上求缘,果上努力~~~~ 作者:每天卷学习,转载请注明原文链接:https://www.cnblogs.com/BlairGrowing/p/14077865.html

  • 相关阅读:
    Leetcode Binary Tree Preorder Traversal
    Leetcode Minimum Depth of Binary Tree
    Leetcode 148. Sort List
    Leetcode 61. Rotate List
    Leetcode 86. Partition List
    Leetcode 21. Merge Two Sorted Lists
    Leetcode 143. Reorder List
    J2EE项目应用开发过程中的易错点
    JNDI初认识
    奔腾的代码
  • 原文地址:https://www.cnblogs.com/BlairGrowing/p/14077865.html
Copyright © 2011-2022 走看看