`

RMQ(Range Minimum/Maximum Query)区间最值查询

 
阅读更多

一、RMQ问题描述 和 几种解题思路

RMQ问题 (Range Minimum/Maximum Query),首先给出一个序列,然后不断询问某个区间内的最大值和最小值。显然,我们在回答所有询问之前,需要根据序列进行一定的预处理。

 

(1)最笨的算法显然是朴素的直接查询 ,回答每个问题是O(n)的。


(2)一种算法是采用线段树 ,即在线段树的每个节点保存该区间的最大值与最小值,O(n)的预处理时间(需要自底向上构建),可以O(logn)地回答每个问题。关于这种解法,见我线段树专题中炮兵阵地那道题即可:)

(3)另一种算法就是神奇的ST算法(Sparse Table)ST算法记录了所有长度为2^f的区间的最值 。以求最大值为例,设v[n][f]表示[ n,n+2^f) 这个区间内的最大值,那么在询问到[a,b)区间的最大值时答案就是max(v[a][f],v[b-2^f][f]),其中f是满足2^f<=b-a的最大的f。至于那张稀疏表,可以用递推的方法在O(nlogn)(也就是表的元素数)的时间内构建。也就是说v[n][f]=max(v[n][f-1],v[n+2^(f-1)][f-1])。

 

下面是我写的C++代码,有待改进:

#include <iostream>
#include <ctime>
#include <cmath>
#include <cstdlib>
using namespace std;

/*
  ST(Sparse Table):    find max/min number in [A,B] within O(nlogn), n=B-A
  构造:v[ a~b ][ 0~ceil( log(B-A)) ]
        init:
             v[i][0]=d[i], i∈[A,B] , 注意:v[i][0]对应前闭后开区间[i, i+2^0)
        recurse:
             v[i][f]=min{ v[i][f-1], v[i-2^(f-1)][f-1] } 
  ---------------------------------------------------------------------------------------------
  prob:
       find max/min in [a,b)∈ [A,B]
  soln:
       max{ v[a][f], v[b-2^f][f] }  <== a+2^f>=b-2^f  <== 2^(f+1)>=b-a, f=0↑的首个满足条件的值
*/

//查询d[A...B]范围内数组的最值 
const int MAX=101;
int cnt=30; //随机交换次数 
const int INF=9999;

int A=2;
int B=30;
int data[MAX];

//ST算法DS: v[][]
int v[MAX][MAX]; 

int minV(int i, int j){
    return (i)<(j)?(i):(j);
}

//给d[]赋予随机数 
void initData(){
     for(int i=0;i<MAX;i++){
         data[i]=i;
     }
     
     //随机交换 
     srand(time(0));
     while(cnt-->0){
         int i= A + (rand()%(B-A));
         int j= A + (rand()%(B-A));
         //cout<<"swap("<<i<<", "<<j<<")"<<endl;
         
         if(i!=j){
             int tmp=data[i];
             data[i]=data[j];
             data[j]=tmp;
         }
     }     
}

void showData(){
     for(int i=A;i<=B;i++){
             cout<<data[i]<<"\t";
     }
     cout<<endl;
}

//构造ST算法DS: v[] 
void initV(){
    //init:
    for(int i=0;i<MAX;i++){
         for(int j=0;j<MAX;j++){
                 v[i][j]=INF;
         }
    }
    for(int i=A;i<=B;i++){
        v[i][0]=data[i];  //当求min时,其余的v[i][0]必须预设为很大的值 
    }
    //DP iteration:
    for(int i=1;i<=ceil( log(B-A)/log(2) );i++){
            for(int j=A;j<=B;j++){
                    v[j][i]=minV( v[j][i-1], v[j+(int)pow((double)2,(double)(i-1))][i-1] );
                    /*
                    cout<<"v["<<j<<"]["<<i<<"]="<<v[j][i]<<"= minV(";
                    cout<<"v["<<j<<"]["<<i-1<<"]="<<v[j][i-1]<<", \t";
                    cout<<"v["<<j+(int)pow((double)2,(double)(i-1))<<"]["<<i-1<<"]="<<v[j+(int)pow((double)2,(double)(i-1))][i-1]<<")"<<endl;                   
                    */
            }
    }  
}

void showV(){
     for(int i=0;i<=ceil( log(B-A)/log(2) );i++){
            for(int j=A;j<=B;j++){
                    cout<<"v["<<j<<"]["<<i<<"]="<<v[j][i]<<"\t";
            }
            cout<<endl<<endl;
     }
     cout<<endl;
}

int query(int a,int b){
    int f=0;
    while( (int)pow((double)2,(double)(f+1)) < (b-a) ){
        f++;
    }
    
    return minV( v[a][f], v[b-(int)pow((double)2,(double)f)][f] );
}

int main()
{
    initData();
    showData();
    
    initV();
    //showV();
    
    int queries;
    cout<<"# of queries:";
    cin>>queries;
    while(queries-->0){
        int a,b;
        cout<<"[a   :";
        cin>>a;
        cout<<"b)   :";
        cin>>b;
        
        cout<<"res : "<<query(a,b)<<endl<<endl;
    }
    
	system("pause");
	return 0;
}        

 

另外,RMQ问题与LCA(Least Common Ancestors,最近公共祖先)问题可以互相转化。LCA问题有一个经典的离线算法Tarjan算法,稍后我将会介绍。

 

二、ST算法

    POJ 3264 Balanced Lineup ==> http://poj.org/problem?id=3264

    代码参考这个大牛的: http://www.cnblogs.com/mjc467621163/archive/2011/08/24/2152133.html

//poj 3264 Balanced Lineup
#include<iostream>        //ST算法,即(Sparse_Table ,稀疏表),可以在O(nlogn)的预处理以后,实现O(1)的查询效率
#include <cmath>
using namespace std;
const int max_n=50005;
int arr[max_n],n,t,l,r;        //原数列arr从下标1开始
int rmq_m[max_n][16],rmq_n[max_n][16];        //16>=log2(50000)==15.609640474436811739351597147447
//rmq_m[i][j],rmq_n[i][j]分别表示原数列arr[i, i+2^j - 1]区间中的最大值和最小值,区间长度为2^j
void rmq_init()        //预处理,采用DP
{
    int i,j;
    //1. DP-INIT:  长度为2^0==1的区间的最值就是单点上的值 
    for(i=1;i<=n;++i) 
        rmq_m[i][0]=rmq_n[i][0]=arr[i];    
        
    //2. DP-ITERATION:   rmq[i][j]=max{ rmq[i][j-1], rmq[i+2^(j-1)][j-1] }
    //         可见 rmq[*][j]=max{ rmq[$][j-1], rmq[#][j-1] } ,因此 j 放在循环外层,且顺序循环 
    for(j=1;j<=log(n+0.0)/log(2.0);++j)  //j表示区间长度为2^j,区间范围[i, i+2^j - 1],小于等于n,所以 j<=log(n+0.0)/log(2.0)
    {
        for(i=1;i+(1<<j)-1<=n;++i)    //因为rmq_m[i][j]表示arr[i, i+2^j - 1]的最大值,所以 i+2^j - 1 <= n
        {
            rmq_m[i][j]=max(rmq_m[i][j-1],rmq_m[i+(1<<(j-1))][j-1]);
            rmq_n[i][j]=min(rmq_n[i][j-1],rmq_n[i+(1<<(j-1))][j-1]);
        }
    }
}
//DP的初值:rmq_m[i][0]其实就等于arr[i]; 状态转移方程:rmq_m[i][j]=max(rmq_m[i][j-1],rmq_n[i+2^(j-1)][j-1])
//采用自底向上的算法递推地给出所有符合条件的rmq_m[i][j]的值
int rmq(int a,int b)    //查询
{
    /*
      要使得[a,b]的max = 
                       max{ [a, a+2^m]的max, [b-2^m+1, m]的max } 
      需要 m 大于一定值: 
                       a+2^m >= b-2^m+1
                <==>   m >= log(b-a+1)/log(2) - 1
                <==>   m 至少是 floor[ log(b-a+1)/log(2) ] 
    */
    int m=log(b-a+1.0)/log(2.0);
    int x=max(rmq_m[a][m],rmq_m[b-(1<<m)+1][m]);    //区间[a,b]最大值
    int y=min(rmq_n[a][m],rmq_n[b-(1<<m)+1][m]);
    return x-y; 
}
//查询从a到b这一段的最大(小)值, 那么我们先求出一个最大的整数m, 使得m满足2^m <= (b - a + 1).
//于是我们就可以把[a, b]分成两个长度为2^m的区间: [a, a+2^m -1], [b-2^m +1, b]; (有部分重叠,b-2^m +1 <= a+2^m -1)
//而rmq_m[a][m]为[a, a+2^m-1]的最大值, rmq_m[b-2^m +1][m]为[b-2^m +1, b]的最大值
int main()
{
    scanf("%d%d",&n,&t);
    for(int i=1;i<=n;++i)
        scanf("%d",arr+i);
    rmq_init();
    while(t--)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",rmq(l,r));
    }
    return 0;
}
 

参考:

问题概述和主要思路:

http://cuitianyi.com/blog/rmq%E9%97%AE%E9%A2%98/

分享到:
评论

相关推荐

    RMQ求区间最值(最大最小)

    区间最值查询(Range Minimum/Maximum Query,简称RMQ)是数据结构和算法领域中一个经典的问题。在处理大规模数据时,我们往往需要快速查询一个序列或者数组在某个连续子区间内的最大值或最小值,而RMQ算法就是为此...

    rsq.rar_RSQ RMQ_range-sketch query

    Structure rmq(range minimum or maximum query) help to find minimum or maximum on segment l and r O(log(l-r+1)) and update O(logN)

    打萎的RMQ 和 LCA

    RMQ(Range Minimum/Maximum Query,区间最值查询)和LCA(Least Common Ancestor,最近公共祖先)是数据结构和算法中的重要概念,它们在解决实际问题中有着广泛的应用。下面将详细介绍这两种算法的基本概念、经典...

    浅谈RMQ(RMQ详解)

    ### RMQ(Range Minimum/Maximum Query)详解 #### 引言 在计算机科学与算法设计领域,RMQ问题,即范围最小/最大查询问题,是一个经典的动态规划与数据结构问题。它涉及到对一段连续的数据进行快速查询,找到该区间...

    RMQ问题求解(ST).pptx

    RMQ(Range Minimum/Maximum Query)问题是在一个给定的数组中寻找某个区间内的最大值或最小值。这种问题在数据结构和算法中非常常见,特别是在处理动态查询时。暴力解决方法是对于每次询问,遍历指定的区间,时间...

    高级数据结构

    RMQ(Range Minimum/Maximum Query)即区间最小/最大查询问题,旨在对一个序列中任意指定区间的最小值或最大值进行查询。一些算法,比如线段树,可以用来解决这个问题。 LCA(Least Common Ancestor)是查找最近...

    倍增法介绍以及应用(RMQ、LCA)

    ST表是一种数据结构,用于解决RMQ(Range Minimum/Maximum Query)问题,即区间最值查询。RMQ问题是指对于长度为n的数列A,回答若干询问RMQ(A, i, j)(i, j ),返回数列A中下标在i, j之间的最小/大值。 例如,给...

    第2章 RMQ-2020-12-07.pdf

    RMQ(Range Minimum/Maximum Query)问题是一种常见的数据结构与算法问题,其核心任务是在一个给定的数组中快速找到指定区间内的最小值或最大值。这类问题在计算机科学和算法竞赛中十分常见,并且具有广泛的应用场景...

    wata ACM 模板 v0.9

    2. RMQ(Range Minimum/Maximum Query)数据结构,用于解决区间最值查询问题。该数据结构可以预处理数组,在O(1)时间复杂度内回答关于区间最值的问题。代码中实现了`RMQ(int[] vs)`构造函数用于初始化数据结构,以及...

    rmq算法(倍增)

    RMQ问题(Range Minimum/Maximum Query,区间最小/大值查询)是计算机科学中一个非常基础且重要的问题。其核心在于快速找到数组中某个指定区间内的最大或最小值。在实际应用中,例如在图像处理、生物信息学以及...

    第2章 RMQ-2021-02-07.pdf

    RMQ问题,全称为Range Minimum Query / Range Maximum Query(区间最小值查询/区间最大值查询),指的是在一个给定的序列A中,对任意指定的区间[i, j](其中1 ≤ i ≤ j ≤ n,n为序列A的长度),找到该区间内元素的...

    RMQ在信息学竞赛中的应用(1)

    RMQ,全称为Range Maximum (Minimum) Query,是信息学竞赛中常见的算法问题,主要用于解决在数组中查询连续子区间最大值或最小值的问题。它通常出现在需要频繁进行区间最值查询的题目中,能够有效地减少计算量,提高...

    RMQ在信息学竞赛中的应用

    RMQ(Range Maximum/Minimum Query)是指在一个固定数组中查询特定区间内的最大值或最小值的问题。这类问题在算法竞赛中非常常见,特别是在那些需要频繁进行区间查询的任务中。 #### 二、RMQ的原理与算法实现 RMQ...

    ACM模板代码库

    - RMQ(Range Minimum/Maximum Query)问题的不同解决算法。 - LCA(Lowest Common Ancestor)问题的离线算法。 - 带权值的并查集。 - 快速排序、堆栈、区间最大频率。 - 归并排序、二分查找以及各种字符串处理算法...

    NOIP夏令营[山东]《倍增》课件

    - **定义**:区间最值查询(Range Minimum/Maximum Query)是指在一个数组中快速找到指定区间的最小值或最大值。 - **倍增思想**:通过预先计算区间长度为\(2^k\)的所有子区间的最小值/最大值,利用这些信息快速...

    算法 for Acmer

    - RMQ(Range Minimum/Maximum Query):求区间最小/大值的算法。 - Treap、划分树:平衡二叉搜索树的变形,用于区间查询。 - 树链剖分:用于处理树上路径查询问题。 - Splay、DLX(舞蹈链):特殊的平衡二叉...

    ACM算法模板(吉林大学).pdf

    - 树状数组、后缀数组、TRIE树(K叉树和左儿子右兄弟模型)、RMQ(Range Minimum/Maximum Query)问题的解决策略。 - LCA(Lowest Common Ancestor)问题的不同解法。 - 带权值的并查集、快速排序、大数运算、最长...

    数据结构算法

    此外,文件内容还提及了RMQ(Range Minimum/Maximum Query)和LCA(Least Common Ancestor)问题。RMQ问题关注于在数组或者序列中寻找最小(或最大)元素的问题,而LCA问题则是在树结构中寻找两个节点的最低公共祖先...

    三峡大学算法设计与分析论文-倍增思想在算法运用与实现

    2.3 **区间极值问题(RMQ,Range Minimum/Maximum Query)**:在一个数组中,查询某个连续子数组内的最小或最大值。倍增算法通过构建ST表(Sparse Table)预先计算每个2的幂次子数组的极值,然后快速获取任意区间内...

    代码仓库培训资料全.docx

    ZKW单点修改指的是在线段树上进行单个元素的修改,RMQ(Range Minimum/Maximum Query)则是在一个区间内查找最小/大值,区间加+赋值是指对某个区间的所有元素进行加法操作或赋新值。 6. **Splay树**:自旋平衡二叉...

Global site tag (gtag.js) - Google Analytics