RMQ (Range Minimum/Maximum Query)问题是指:

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

for循环遍历一边,然后输出,那么你很容易想到会被T飞掉;

1、先写一种比较高效的ST算法解决这个问题。

线段树预处理O(nlogn),查询O(logn),支持在线修改 
ST表预处理O(nlogn),查询O(1),但不支持在线修改

其实ST表是一种动态规划的思想;每次运用倍增思想求解每一个区间;

一个序列的子区间有o(n^2)个,根据倍增思想,我们首先在这个规模中选择一些2的正实数次幂作为代表值;

定义:f(i,j)表示[i,i+2j−1] 这段长度为2j 的区间中的最大值。也就是从i开始2j个数的最大值;

边界:f(i,0)=ai。即f[i,i]区间的最大值就是ai。

递推时我们把子区间的长度成倍增长,$f(i,j)=max{f(i,j−1),f(i+2j−1,j−1)}$;

状态转移:将[i,j]平均分成两段,一段为[i,i+2j−1],另一段为[i+2j−1,i+2j−1]。

即两段子区间的长度均为2j−1。f(i,j)的最大值即这两段的最大值中的最大值。

查询任意区间[l,r]时,我们先计算出一个k,满足2k<r-l+1<=2k+1;也就是使2k小于区间的前提下的最大的k,

那么“从l开始2k个数”和以r结尾的2k个数,这两段一定覆盖了整个区间[l,r],

这两段的最大值分别是f[l,k],f[r-2k+1,k],两个中较大的就是整个区间[l,r]的最值,即使重叠也没有关系;

网上也有这样解释的;https://blog.csdn.net/Hanks_o/article/details/77547380

一个查询最小值的问题:质量检测

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int n,m,f[][],x,y;
inline void Rmq(int x)
{
for(int i=; i<=; i++)
{
for(int j=; j<=x; j++)
{
if(j+(<<i)-<=x)//使j+(1<<i)-1不超过区间最右端;
f[j][i]=min(f[j][i-],f[j+(<<(i-))][i-]);//动态规划;
}
}
}
inline int query(int x,int y)
{
int k=log2(y-x+);
return min(f[x][k],f[y-(<<k)+][k]);
}
int main()
{
read(n);read(m);
for(int i=;i<=n;i++)
read(f[i][]);
Rmq(n);
for(int i=;i<=n-m+;i++)
printf("%d\n",query(i,i+m-));
return ;
}

毕竟最大最小改一下min,max就可以了;

忠诚

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int n,m,f[][],x,y,a[];
inline void Rmq(int x)
{
for(int i=; i<=; i++)
{
for(int j=; j<=x; j++)
{
if(j+(<<i)-<=x)
f[j][i]=min(f[j][i-],f[j+(<<(i-))][i-]);
}
}
}
inline int query(int x,int y)
{
int k=log2(y-x+);
return min(f[x][k],f[y-(<<k)+][k]);
}
int main()
{
read(n);read(m);
for(int i=;i<=n;i++)
read(f[i][]);
Rmq(n);
for(int i=;i<=m;i++)
{
read(x);read(y);
a[i]=query(x,y);
}
for(int i=;i<=m;i++)
printf("%d ",a[i]);
return ;
}

但有点变态的是,这个过不了洛谷的1440,所以我们再来讲一下单调队列的实现方式;

例题:滑动窗口

这个单调队列讲解好强:https://www.luogu.org/blog/hankeke/solution-p1886

本题样例:

8 3
1 3 -1 -3 5 3 6 7

下文中我们用q来表示单调队列,p来表示其所对应的在原列表里的序号。

  1. 由于此时队中没有一个元素,我们直接令1进队。此时,q={1},p={1}。

  2. 现在3面临着抉择。下面基于这样一个思想:假如把3放进去,如果后面2个数都比它大,那么3在其有生之年就有可能成为最小的。此时,q={1,3},p={1,2}

  3. 下面出现了-1。队尾元素3比-1大,那么意味着只要-1进队,那么3在其有生之年必定成为不了最小值,原因很明显:因为当下面3被框起来,那么-1也一定被框起来,所以3永远不能当最小值。所以,3从队尾出队。同理,1从队尾出队。最后-1进队,此时q={-1},p={3}

  4. 出现-3,同上面分析,-1>-3,-1从队尾出队,-3从队尾进队。q={-3},p={4}。

  5. 出现5,因为5>-3,同第二条分析,5在有生之年还是有希望的,所以5进队。此时,q={-3,5},p={4,5}

  6. 出现3。3先与队尾的5比较,3<5,按照第3条的分析,5从队尾出队。3再与-3比较,同第二条分析,3进队。此时,q={-3,3},p={4,6}

  7. 出现6。6与3比较,因为3<6,所以3不必出队。由于3以前元素都<3,所以不必再比较,6进队。因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时,q={3,6},p={6,7}

  8. 出现7。队尾元素6小于7,7进队。此时,q={3,6,7},p={6,7,8}。

那么,我们对单调队列的基本操作已经分析完毕。因为单调队列中元素大小单调递*(增/减/自定义比较),因此,队首元素必定是最值。按题意输出即可。

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int head,tail,q[],p[],k,n,a[];
inline void maxn()
{
head=;tail=;
for(int i=;i<=n;i++)
{
while(head<=tail&&q[tail]<=a[i])
--tail;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k) printf("%d ",q[head]);
}
putchar('\n');
}
inline void minn()
{
head=;tail=;
for(int i=;i<=n;i++)
{
while(head<=tail&&q[tail]>=a[i])
--tail;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k) printf("%d ",q[head]);
}
putchar('\n');
}
int main()
{
read(n);read(k);
for(int i=;i<=n;i++)
read(a[i]);
minn();
maxn();
return ;
}

模板 RMQ问题ST表实现/单调队列的更多相关文章

  1. 使用类模板的C++线性表实现(数组方式)

    main.h #ifndef _MAIN_H_ #define _MAIN_H_ #include <iostream> #include <exception> #inclu ...

  2. POJ 2823 Sliding Window​ (模板题)【单调队列】

    <题目链接> <转载于>>> > 题目大意: 给你一段序列和一个长为k的窗口,这个窗口从最左边逐渐向右滑,直到滑到最右边,问你,该窗口在滑动的过程中,最大值和 ...

  3. 洛谷 P1886 滑动窗口(单调队列)

    嗯... 题目链接:https://www.luogu.org/problem/P1886 首先这道题很典型,是标准的单调队列的模板题(也有人说单调队列只能解决这一个问题).这道题可以手写一个队列,也 ...

  4. POJ1821 单调队列//ST表 优化dp

    http://poj.org/problem?id=1821 当我们在考虑内层循环j以及决策k的时候,我们可以把外层变量i看作定值,以此来优化dp状态转移方程. 题意 有n个工人准备铺m个连续的墙,每 ...

  5. 【模板】deque实现单调队列

    双端队列deque容器: 关于deque最常用的有这几个函数: 都是成员函数 双端队列模板题:[洛谷]P2952 [USACO09OPEN]牛线Cow Line #include<iostrea ...

  6. HDU - 5289 Assignment (RMQ+二分)(单调队列)

    题目链接: Assignment  题意: 给出一个数列,问其中存在多少连续子序列,使得子序列的最大值-最小值<k. 题解: RMQ先处理出每个区间的最大值和最小值(复杂度为:n×logn),相 ...

  7. HDU3183 A Magic Lamp —— 贪心(单调队列优化)/ RMQ / 线段树

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183 题解: 方法一:贪心. 在草稿纸上试多几次可以知道,删除数字中从左到右最后一位递增(可以等于)的 ...

  8. [Bzoj4540][Hnoi2016] 序列(莫队 + ST表 + 单调队列)

    4540: [Hnoi2016]序列 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1567  Solved: 718[Submit][Status] ...

  9. cf1208 E Let Them Slide(差分+RMQ\单调队列)

    题意 如题目的图所示,每行都可以左右移动,但是数字不允许断开,且不许越界(宽度为w). 单独求每一列的最大的和为多少. 思路 对于每一列来说,在每一行上都有一个可以取到的区间, 所以,对于一列来说,答 ...

随机推荐

  1. 单片机stm32小白入门级学习路线“图”

     学习stm32的是真的越来越多了,当然我也是其中语言,所以对于stm32的学习路线非常的感兴趣,所以我也分享一下  虽然是盗图吧  不过也算是分享 ,下边有觉得不错的视频资料  也奉上   (stm ...

  2. js基础 -函数

    函数 定义 var a =function (){...}; 匿名函数方式定义function a(){} 直接定义 函数的参数arguments 可以接收任意个参数,是个像数组的内容,可for in ...

  3. HTML+CSS+JavaScript-案例

    CSS-flex弹性布局案例1: HTML代码: <!DOCTYPE html> <html lang="en"> <head> <met ...

  4. uCOS-II

    /****************************************************/ **关于移植,ucos官网上给的有template,主要思想是实现任务切换的两个函数(任务 ...

  5. wordCount剖析Spark模型

  6. Tea for Mac(mac笔记软件)中文版

    为大家分享一款好用且免费的mac笔记软件,Tea for Mac提供了实时渲染的Markdown,功能全面,支持各种快捷键,使用tea mac版时,在段首打@即可快速插入图片.标题.列表等元素,非常便 ...

  7. java mysql连接时出现的问题

    当出现Caused by: java.sql.SQLException: Unknown system variable ‘tx_isolation’ 一般是mysql-connector-java的 ...

  8. C++实验三

    part2 graph.h #ifndef GRAPH_H#define GRAPH_H// 类Graph的声明 class Graph { public: Graph(char ch, int n) ...

  9. Matlab文件和数据的导入与导出

    ref: https://blog.csdn.net/zengzeyu/article/details/72530596 Matlab文件和数据的导入与导出 2017年05月19日 15:18:35  ...

  10. “QObject调用moveToThread()后 该如何释放”及QThread 的启动关闭

    1 QThread *thread = new QThread( ); 2 Task *task = new Task(); 3 task->moveToThread(thread); 4 co ...