【RMQ】【Sparse_Table算法】

定义:

RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:

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

此类问题的解决方法有很多,暴力(当然也就说说,基本没有出题的会让你暴过去)、线段树(复杂度为O(nlogn))、以及一个非常有名的在线处理RMQ问题的算法 -- Sparse_Table算法,简称ST算法,该算法能在进行O(nlogn)的预处理后达到查询O(1)的效率。主要思想为dp。

(一)预处理,DP

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

例如有A[10] = 3 2 4 5 6 8 1 2 9 7;

初值:

dp[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;

并且我们可以容易的看出dp[i][0] = A[i]。

状态转移方程:

我们把dp[i][j]分成长度相同且为2j-1的两段,一段为dp[i][2j - 1], 一段为dp[i+2j - 1][2j - 1]。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。

dp[i,j]就是这两段各自最大值中的最大值。于是我们得到了状态转移方程:

  dp[i][j] = max(dp[i][j-1], dp[i + 2(j-1)][j-1];

1 /*预处理->O(nlogn)*/
2 void initRMQ(int n)
3 {
4     for(int i = 1; i <= n; i++) dp[i][0] = A[i];
5     for(int j = 1; j < 20; i++)
6         for(int i = 1; i <= n; j++)
7             dp[i][j] = max(dp[i][j-1], dp[i + (1 << (j-1))][j-1]);
8 }

    注意代码中循环的顺序,外层是j,内层是i!!

(二)查询

若查询区间为A[i, j],那么我们需要找到覆盖这个闭区间(左边界取i,右边界取j)的最小幂(可重叠,比如查询A[5,9],我们可以查询A[5678]和A[6789])。

因为这个区间的长度len = j - i + 1,所以我们可以取k=log2(len),则有:RMQ(A, i, j) = max{ dp[i][k], dp[j-2k+1][k] }。

例如,查询区间[2,8]的最大值,k = log2(8 - 2 + 1) = 2,即求max( dp[2][2],dp[8 - 2 ^ 2 + 1][2]) = max(F[2, 2],F[5, 2]);

int Log2[maxn];
void init()
{
    Log2[0] = -1; Log2[1] = 0;
    int i, j;
    for(i = 2, j = 0; i < maxn; i++)
    {
        if(i > (1<<(j+1))) j++;
        Log2[i] = j;
    }
}

int query(int L, int R)
{
    if(L > R) swap(L, R);
    if(L == R) return dp[L][0];
    int len = R-L+1;
    int k = Log[len]; //此处用的是Log2[]数组保存每个数的log值,当然也可以用下面的式子求得;
    //int k = (int)(log(des - src + 1.0) / log(2.0));
    return max(dp[L][k], dp[R-(1<<k)+1][k]);
}
时间: 2024-11-04 08:00:45

【RMQ】【Sparse_Table算法】的相关文章

RMQ 问题之Sparse_Table算法

RMQ问题,全名(Range Minimum/Maximum Query),是求给定区间中的最值问题. 主要方法及复杂度如下: 1.朴素(即搜索),O(n)-O(qn) online. 2.线段树,O(n)-O(qlogn) online. 3.Sparse_Table(实质是动态规划),O(nlogn)-O(1) online. 4.RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(1) online. 昨天刚刚学了第三种,ST算

poj 3264 区间最大最小值 RMQ问题之Sparse_Table算法

Balanced Lineup Time Limit: 5000 MS Memory Limit: 0 KB 64-bit integer IO format: %I64d , %I64u Java class name: Main [Submit] [Status] [Discuss] Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order

LCA上的RMQ模板算法

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 10764    Accepted Submission(s): 3923 Problem Description There are n houses in the village and some bidirectional roads connecting

[POJ3264]Balanced Lineup(RMQ, ST算法)

题目链接:http://poj.org/problem?id=3264 典型RMQ,这道题被我鞭尸了三遍也是醉了…这回用新学的st算法. st算法本身是一个区间dp,利用的性质就是相邻两个区间的最值的最值一定是这两个区间合并后的最值,这条性质决定了这个dp子问题的重叠.可以利用这个性质预处理出这张表,只不过步长是2的幂次. 查询的时候也是如此,但是未必会精准地选中两个区间,不要紧,因为两个区间重叠的部分也会被自动算在求最值的内部.这个时候如果算的是区间和的话,要减去这一部分.(区间和的话直接用前

LCA最近公共祖先 ST+RMQ在线算法

对于这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O(n); 先介绍在线算法: 1) dfs: 对于图所示的树,我们从根节点1开始dfs,按照先序访问(不算完全的先序),那么它访问顺序就是1 -> 2 -> 4 -> 2 -> 5 -> 7 -> 5 -> 8 -> 5 -> 2 -> 1 -> 3 -> 1 然后用数组first存第一次访问到该点时的时间(也就是访问顺序里面

POJ 3264 区间最大最小值Sparse_Table算法

题目链接:http://poj.org/problem?id=3264 Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 47515   Accepted: 22314 Case Time Limit: 2000MS Description For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up i

POJ 3368 Frequent values RMQ ST算法/线段树

                                                     Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 15229   Accepted: 5550 Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In

CF359D Pair of Numbers [RMQ+ST算法]

题意: 给一串数,找出最长的区间使得这个区间里面有个数能被其他所有数整除(包括它自己),求满足这个条件的最长区间的个数及长度,以及这些区间的左端的位置 分析: 这个区间的要求其实就是GCD(ALL)=MIN(ALL),能被其他数整除,这个数肯定是最小的,然后又能被其他数整除(包括自己)这个数就是GCD了 可以二分枚举区间长度,然后验证答案的可靠性 对当前长度的所有区间,套用RMQ,验证是否存在一个区间的GCD=MIN 如果有这样的一个区间,那么说明当前长度可以,加大枚举的区间长度,否则减小 #i

自己写的 RMQ ST算法模板类

1 #include<iostream> 2 using namespace std; 3 #include<cstdio> 4 #include<cstring> 5 /* 6 说明: 7 RMQ<T> rr;定义一个查询区间最小值的数据类型为T 的类 8 SetMaxn(T maxn);设置初始化数组的最大值 9 Creat(T a[],int maxn) 设置查询的数组,和数组长度,从0开始 10 Getx(int la,int lb) 查询数组中下标