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])<<" ";
}
}

最新文章

  1. 16、java中的异常处理机制
  2. SQL Server调优系列基础篇(并行运算总结)
  3. 剑指Offer 从尾到头打印链表
  4. Java 动态代理
  5. svn 403 Forbidden
  6. 剑指offer系列21--二叉搜索树的后续遍历序列
  7. 取消界面的title
  8. dom树的介绍,及原理分析
  9. 用MATLAB画函数的曲线
  10. 自制证书搭建https服务
  11. IIS Express中如何配置支持json
  12. [原创]浅谈如何使用gcc开发NT核心驱动程序
  13. Kmeans聚类算法
  14. 深入学习Java8 Lambda (default method, lambda, function reference, java.util.function 包)
  15. [daily] cscope
  16. 数据挖掘---Matplotib的学习
  17. lower_bound函数与upper_bound函数
  18. 如何使用Visual Studio 2017调试.net库源代码
  19. Jmeter(三十二)_搭建本地接口自动化环境
  20. javaScript之表格操作&lt;一:新增行&gt;

热门文章

  1. HandlerInterceptor与WebRequestInterceptor的异同
  2. SQL 练习4
  3. 题解 Merchant
  4. 回忆(一):反射中获得class对象的三种方法
  5. Python代码阅读(第2篇):数字转化成列表
  6. 及上一篇linux安装mysql的说明
  7. vue 基础入门(一)
  8. Ubuntu 配置、使用samba共享文件夹
  9. VS2017 Debug时候出现 Script Error An error has occurred in the script on this page. 解决办法
  10. MySQL 实例空间使用率过高的原因和解决方法