RMQ问题

版权声明:转载请注明出处 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个数起连续2^{l}个数中的最大(小)值。

那么初始化的内容就显而易见了: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);
    }//查询的时候也是要在四块中找
}

​

猜你喜欢

转载自blog.csdn.net/qq_39541141/article/details/85224768
今日推荐