RMQ总结

       以下摘自刘汝佳大神的《训练指南》。范围最小值问题(Range Minimum/Maximum Query,RMQ)。给出一个n个元素的数组A1,A2……An,设计一个数据结构,支持查询操作Query(L,R):计算min{A(L),A(L+1),……,A(R)}

       Sparse-Table算法能够很好的解决这个问题,它的预处理时间是O(nlogn),但是查询只需要O(1),而且常数很小。最重要的是,这个算法非常好写,并且不易写错。

       令d[i,j)表示从i开始的,长度为2j的一段元素中的最小值,则可以用递推的方法计算d(i,j):d(i,j)=min{d(i,j-1),d(i+2j – 1,j-1)}。注意2j<=n,因此d数组元素的个数不超过nlogn,而每一项都可以在常数时间计算完毕,故总时间为O(nlogn)。代码如下。

void RMQ_init()

{

    int i,j;

    for(i=0; i<n; i++)

        d[i][0]=a[i];

    for(j=1; (1<<j)<=n; j++)

        for(i=0; i+(1<<j)-1<n; i++)

            d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);

}

查询操作很简单,令k为满足2k<=R-L+1的最大整数,则以L开头、以R结尾的两个长度为2k的区间合起来即覆盖了查询区间[L,R]。由于是取最小值,有些元素重复考虑了几遍也没关系。

查询代码如下。

int RMQ(int L,int R)

{

    int k=0;

    while((1<<(k+1))<=R-L+1) k++;

    return min(d[L][k],d[R-(1<<k)+1][k]);

}

  

POJ3264 Balanced Lineup

题目大意

给定一个序列A1,A2,…An-1,An。并给出一些查询,对于每次查询(L,R),要求你返回序列[L,R]中最小值与最大值的差

题解

赤裸裸的RMQ。。。

代码:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#define MAXN 50005

using namespace std;

int a[MAXN],n;

int ma[MAXN][20],mi[MAXN][20];

void RMQ_init()

{

    int i,j;

    for(i=0;i<n;i++)

    {

         ma[i][0]=a[i];

         mi[i][0]=a[i];

    }

    for(j=1;(1<<j)<=n;j++)

    for(i=0;i+(1<<j)-1<n;i++)

    {

        ma[i][j]=max(ma[i][j-1],ma[i+(1<<(j-1))][j-1]);

        mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);

    }

}

int RMQ(int L,int R)

{

    int k=0;

    while((1<<(k+1))<=(R-L+1)) k++;

    return max(ma[L][k],ma[R-(1<<k)+1][k])-min(mi[L][k],mi[R-(1<<k)+1][k]);

}

int main(void)

{

    int i,Q,l,r;

    memset(mi,0,sizeof(mi));

    memset(ma,0,sizeof(ma));

    scanf("%d%d",&n,&Q);

    for(i=0;i<n;i++)

    scanf("%d",&a[i]);

    RMQ_init();

    while(Q--)

    {

        scanf("%d%d",&l,&r);

        printf("%d\n",RMQ(l-1,r-1));

    }

    return 0;

}

 

POJ2019 Cornfields

题目大意

给定一个大小为N*N矩阵,给出一些查询,对于每次查询(x,y),要求你返回以左下角(x,y)开始的长宽都为B的子矩阵中的最大值与最小值的差

题解

最标准的二维RMQ

代码:

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#define MAXN 255

using namespace std;

int Min[MAXN][MAXN][9][9],Max[MAXN][MAXN][9][9],a[MAXN][MAXN];

int n;

void init_RMQ()

{

    int i,j,ii,jj;

    for(i=1; i<=n; i++)

        for(j=1; j<=n; j++)

        {

            Min[i][j][0][0]=a[i][j];

            Max[i][j][0][0]=a[i][j];

        }

    for(j=0; (1<<j)<=n; j++)

        for(jj=0; (1<<jj)<=n; jj++)

        {

            if(j==0&&jj==0) continue;

            for(i=1; i+(1<<j)-1<=n; i++)

                for(ii=1; ii+(1<<jj)-1<=n; ii++)

                {

                    if(j==0)

                    {

                        Min[i][ii][j][jj]=min(Min[i][ii][j][jj-1],Min[i][ii+(1<<(jj-1))][j][jj-1]);

                        Max[i][ii][j][jj]=max(Max[i][ii][j][jj-1],Max[i][ii+(1<<(jj-1))][j][jj-1]);

                    }

                    else

                    {

                        Min[i][ii][j][jj]=min(Min[i+(1<<(j-1))][ii][j-1][jj],Min[i][ii][j-1][jj]);

                        Max[i][ii][j][jj]=max(Max[i+(1<<(j-1))][ii][j-1][jj],Max[i][ii][j-1][jj]);

                    }

                }

        }

}

int RMQ(int L1,int R1,int L2,int R2)

{

    int k=0,max1,max2,min1,min2;

    while(1<<(k+1)<=L2-L1+1) k++;

    max1=max(Max[L1][R1][k][k],Max[L2-(1<<k)+1][R1][k][k]);

    max2=max(Max[L1][R2-(1<<k)+1][k][k],Max[L2-(1<<k)+1][R2-(1<<k)+1][k][k]);

    min1=min(Min[L1][R1][k][k],Min[L2-(1<<k)+1][R1][k][k]);

    min2=min(Min[L1][R2-(1<<k)+1][k][k],Min[L2-(1<<k)+1][R2-(1<<k)+1][k][k]);

    return max(max1,max2)-min(min1,min2);

}

void init()

{

    int i,k,j,b,x,y;

    while(scanf("%d%d%d",&n,&b,&k)!=EOF)

    {

        for(i=1; i<=n; i++)

            for(j=1; j<=n; j++)

                scanf("%d",&a[i][j]);

        init_RMQ();

        while(k--)

        {

            scanf("%d%d",&x,&y);

            printf("%d\n",RMQ(x,y,x+b-1,y+b-1));

        }

    }

}

int main(void)

{

    init();

    return 0;

}

你可能感兴趣的:(总结)