RMQ (Range Minimum/Maximum Query)问题

来源:互联网 发布:怎么用记事本编程c语言 编辑:程序博客网 时间:2024/05/20 00:38

原始博文地址:点击打开链接

问题描述
 RMQ问题是求给定区间中的最值问题。对于长度为n的数列A,回答若干查询RMQ(A, i, j)。返回数组A中下标在[i,j]里的最小值的下标。比如数列 5,8,1,3,6,4,9,5,7      那么RMQ(2,4) = 3, RMQ(6,9) = 6.

最简单的解法时间复杂度是O(n),就是对于每一个查询遍历一遍数组。但是当n非常大的时候,并且查询次数非常多的时候,这个解决方案就不是那么高效了。

解决RMQ最强大的算法——Sparse-Table算法:
 Sparse-Table算法是一个在线算法,所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。
首先是预处理,用动态规划(DP)解决。设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5 6 8 1 2 9 7,F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。 F[1,2]=5,F[1,3]=8,F[2,0]=2,F[2,1]=4……从这里可以看出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为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段的最大值中的最大值。于是我们得到了动态规划方程F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])。
然后是查询。取k=[log2(j-i+1)],则有:RMQ(A, i, j)=min{F[i,k],F[j-2^k+1,k]}。 举例说明,要求区间[2,8]的最大值,总共2到8是7个元素,所以k=2,那么就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。


算法伪代码
//初始化
 
INIT_RMQ
 
//max[i][j]中存的是从i开始的2^j个数据中的最大值,最小值类似,num中存有数组的值
 
for i := 1 to n
 
  max[i][0] = num[i]
 
for j := 1 to log(n)/log(2)
 
  for i := 1 to (n+1-2^j)
 
     max[i][j] = MAX(max[i][j-1], max[i+2^(j-1)][j-1])
 
//查询
 
RMQ(i, j)
 
k = log(j-i+1) / log(2)
 
return MAX(max[i][k], max[j-2^k+1][k])


实际一道题:点击打开链接

题目1544:数字序列区间最小值
时间限制:1 秒内存限制:128 兆特殊判题:否
题目描述:
给定一个数字序列,查询任意给定区间内数字的最小值。
输入:
输入包含多组测试用例,每组测试用例的开头为一个整数n(1<=n<=100000),代表数字序列的长度。
接下去一行给出n个数字,代表数字序列。数字在int范围内。
下一行为一个整数t(1<=t<=10000),代表查询的次数。
最后t行,每行给出一个查询,由两个整数表示l、r(1<=l<=r<=n)。
输出:
对于每个查询,输出区间[l,r]内的最小值。
样例输入:
5
3 2 1 4 3
3
1 3
2 4
4 5
样例输出:
1
1
3

code:

#include <iostream>#include <cmath>#include <cstdio>using namespace std;const int SIZE=100001;int a[SIZE], n;int f[SIZE][20];void RMQ(){    int logj=log(n)/log(2);    for(int i=1;i<=n;i++) f[i][0]=a[i];    for(int j=1;j<=logj;j++)        for(int i=1;i<=n+1-(1<<j);i++)        {            f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);        }}int quireMin(int l, int r){    int k=log(r-l+1)/log(2);    return min(f[l][k],f[r-(1<<k)+1][k]);}int main(){    int t,l,r;    while(scanf("%d",&n)!=EOF)    {        for(int i=1;i<=n;i++) scanf("%d",&a[i]);        RMQ();        scanf("%d",&t);        while(t--)        {            scanf("%d %d",&l,&r);            printf("%d\n",quireMin(l,r));        }    }    return 0;}


原创粉丝点击