版权声明:转载请注明出处 https://blog.csdn.net/qq_39541141/article/details/85224768
一些废话
难得闲暇的一个周日下午
用了三个小时学了一下 一维和二维的RMQ ,作文以记之 雾)
正题:
什么是RMQ问题?
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题
算法思路:
通过复杂度为O(nlogn)的时间预处理后,以复杂度O(1)找出一段区间内的最大(小)值
预处理
a[i]是原数列的数值
f[i, l]表示从第i个数起连续个数中的最大(小)值。
那么初始化的内容就显而易见了:f[i,0]就等于a[i]
在写dp时,回想一下复杂度为nlogn,便知道其中一层循环是2的指数级增长
那么 代码如下
for(register int i=1;i<=k;i++)
for(register int l=1;l<=n;l++)
if(l+(1<<i)-1<=n)
f[l][i]=min(f[l][i-1],f[l+(1<<(i-1))][i-1]);
上面的k也就是一个2的最大指数,根据题意来定
一般10的6次方k取20刚刚好
查询
求最值并不像求和一样必须是刚刚好组成,而可以有重叠的部分
那么,我们便可以得到
RMQ(l, r)=min(f[l][k],f[r-(1<<k)][k])
这里的k又是什么?????
其实这里的k表示不大于n的2的最大次幂
可以这样求出k
int k=0;
while(1<<(k+1)<=r-l+1)k++;
代码
void RMQ(int t){
for(register int i=1;i<=n;i++)f[i][0]=a[i];
for(register int i=1;i<=21;i++)
for(register int l=1;l<=t;l++)
if(l+(1<<i)-1<=t)
f[l][i]=min(f[l][i-1],f[l+(1<<(i-1))][i-1]);
}
int get(int l,int r){
int k=0;
while(1<<(k+1)<=r-l+1)k++;
return min(f[l][k],f[r-(1<<k)][k]);
}
例题
P2251 质量检测
就是一个裸的模板
#include<iostream>
#include<cstdio>
#define maxn 105000
using namespace std;
int a[maxn],f[maxn][22],n,m;
inline int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x*=10,x+=ch-'0',ch=getchar();
return x;
}
void csh(int t){
for(register int i=1;i<=n;i++)f[i][0]=a[i];
for(register int i=1;i<=20;i++)
for(register int l=1;l<=t;l++)
if(l+(1<<i)-1<=t)
f[l][i]=min(f[l][i-1],f[l+(1<<(i-1))][i-1]);
}
int find(int l,int r){
int k=0;
while(1<<(k+1)<=r-l+1)k++;
return min(f[l][k],f[r-(1<<k)][k]);
}
int main()
{
cin>>n>>m;
for(register int i=1;i<=n;i++)a[i]=read();
csh(n);
for(register int i=1;i<=n-m+1;i++)
cout<<find(i,i+m)<<endl;
return 0;
}
二维RMQ
其实思路是一模一样的
先放一下代码吧 要是懂了一维二维就不难理解
可以试着A一下POJ 2019 Cornfields
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
int dpi[300][300][9],dpa[300][300][9];
//因为题目中说是正方形,便可以省略一维,最后的9表示编边长为2的几次方;
int main()
{
int n,b,k,i,j,ii,jj,x,y;
int maxa,mina;
scanf("%d%d%d",&n,&b,&k);
memset(dpi,0,sizeof(dpi));
memset(dpa,0,sizeof(dpa));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
scanf("%d",&x),dpa[i][j][0]=x,dpi[i][j][0]=dpa[i][j][0];//初始化最大值最小值
for(ii=1;ii<9;ii++)
{
for(i=0;i+(1<<ii)-1<n;i++)
{
for(j=0;j+(1<<ii)-1<n;j++)//相当于省略了上面代码中的if语句
{
dpi[i][j][ii]=min(dpi[i][j][ii-1],dpi[i+(1<<(ii-1))][j][ii-1]);
dpi[i][j][ii]=min(dpi[i][j][ii],dpi[i][j+(1<<(ii-1))][ii-1]);
dpi[i][j][ii]=min(dpi[i][j][ii],dpi[i+(1<<(ii-1))][j+(1<<(ii-1))][ii-1]);
dpa[i][j][ii]=max(dpa[i][j][ii-1],dpa[i+(1<<(ii-1))][j][ii-1]);
dpa[i][j][ii]=max(dpa[i][j][ii],dpa[i][j+(1<<(ii-1))][ii-1]);
dpa[i][j][ii]=max(dpa[i][j][ii],dpa[i+(1<<(ii-1))][j+(1<<(ii-1))][ii-1]);
}
}
}//相当于分成了四部分去求最值
int kk=log(1.0*b)/log(2.0);//这是求k值的过程
for(i=0; i<k; i++)
{
scanf("%d%d",&x,&y);
x--,y--;
maxa=dpa[x][y][kk];
maxa=max(maxa,dpa[x][y+b-(1<<kk)][kk]);
maxa=max(maxa,dpa[x+b-(1<<kk)][y][kk]);
maxa=max(maxa,dpa[x+b-(1<<kk)][y+b-(1<<kk)][kk]);//
mina=dpi[x][y][kk];
mina=min(mina,dpi[x][y+b-(1<<kk)][kk]);
mina=min(mina,dpi[x+b-(1<<kk)][y][kk]);
mina=min(mina,dpi[x+b-(1<<kk)][y+b-(1<<kk)][kk]);//
printf("%d\n",maxa-mina);
}//查询的时候也是要在四块中找
}