RMQ问题的两种解法
1.ST表
这种算法是建立在倍增的原理上的,它可以做到O(logn)预处理,O(1)作答
但是它的缺点是很容易超空间,最大能处理的数只有1e6级别
-
算法原理
首先,我们维护一个二位数组,第一个参数是n的大小,第二个参数是19(20也可以)
这个数的设置是log(max(a[i]))
我们将所有数存入 f[i][0]中
接下来,根据你的数组的第二个参数为上限开始循环max[i][j]表示i位置到(i+2^j)中数列的最大值
例如Max[i][1]表示的是i位置和i+1位置中两个数的最大值我们从1开始,每次将相邻两个区间合并,那么就是每次区间长度都会乘2,要处理的数量都会/2,那么复杂度就是O(logn)
for(int j=1;j<=21;j++){ for(int i=1;i+(1<<j)-1<=n;i++){ f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); } }
每次合并将区间(i 到 i+2^(j-1) -1)与( i+2^(j -1) 到 i+2^(j)-1)合并
查询的时候是
图片来源 -
核心代码
int query(int x){int query(int l,int r){ int k=log2(r-l+1); return max(f[l][k],f[r-(1<<k)+1][k]); } for(int i=1;i<=n;i++) f[i][0]=read(); for(int j=1;j<=21;j++) for(int i=1;i+(1<<j)-1<=n;i++) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
2.单调队列
这里介绍用STL双端对立来实现
-
创建双端队列
deque dq;
-
判断是否为空
dq.empty(); -
访问队头
dq.front() -
访问队尾
dq.back(); -
加入一个新元素
dq.push_back();
dq.push_front(); -
弹出
dq.pop_back();
dq.pop_front();2.算法逻辑
删尾:
我们每拿到一个数的时候,我们将它和队尾作比较,如果比队尾大,就删除队尾
知道目前的这个数严格大于插入的这个数
去头:
如果队头离我们正在处理的i太远了,我们就把它去掉。
队列头就是目前区间的最大值。3.核心代码
cin>>n>>m; for(int i=0;i<n;i++){ scanf("%d",&a[i].v); a[i].id=i; } ans[0]=0; for(int i=1;i<n;i++){ while(!dq.empty() && dq.back().v>=a[i-1].v) dq.pop_back(); dq.push_back(a[i-1]); while(dq.front().id<i-m) dq.pop_front(); ans[i]=dq.front().v; } for(int i=0;i<n;i++){ printf("%d\n",ans[i]); }