zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    ch2_CaseStudy_CanonicalNASLScript.txt
    树形数据广度排序处理示例.sql
    编程管理SQL SERVER的帐号.sql
    索引影响查询结果顺序的示例
    校验表中数据是否有循环编码的通用存储过程.sql
    BackupAndRestoreSmallWorks.sql
    cPanel下安装GodaddySSL教程
    ExcludingRNG.cs
    SymmetricAlgorithmEvaluator.cs
    操作SQLSERVERAGENT服务的扩展存储过程.sql
  • 原文地址:https://www.cnblogs.com/zjbztianya/p/3061145.html
Copyright © 2011-2022 走看看