zoukankan      html  css  js  c++  java
  • 51NOD1174 区间最大数 && RMQ问题(ST算法)

     

     

    RMQ问题(区间最值问题Range Minimum/Maximum Query)

        ST算法

        RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i,j之间的最小/大值。如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来。在这里介绍一个在线算法。所谓在线算法,是指用户每输入一个查询便马上处理一个查询。该算法一般用较长的时间做预处理,待信息充足以后便可以用较少的时间回答每个查询。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

        假设a数组为:

        1, 3, 6, 7, 4, 2, 5

        1.预处理

        设dp[i][j]表示从第i位开始连续2^j个数中的最大值。例如dp[2][1]为第2位数开始连续2个的数的最大值,即6, 4之间的最大值,即mn[2][1] = 4。我们先初始化dp[0...n-1][0]为a数组中的值,之后我们很容易想到递推方程:

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

    
    

      20000> 2**14 > 10000
      30000> 2**15 > 40000
      60000> 2**16 > 70000


    1
    for(int j = 0; j < 20; j ++) 2 for(int i = 1; i + (1 << j) <= n + 1; i ++) 3 dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);

        2.查询

        假设我们需要查询[lo, hi]之间的最值,我们令k = log2(hi-lo+1)

        由于k是 log2(hi-lo+1)向下取整得到的,所以无法完全覆盖lo到hi

        只要保证lo + k 的长度大于lo到hi的长度的1/2即可使用:RMQ[l, r] = min(dp[l][k], dp[r - (1 << k) + 1][k]);

        

        因为不难看出,对于任意长度len,(int)log2(len) ** 2 > len/2

        所以最终代码如下:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<cmath>
     5 using namespace std;
     6 /*
     7 20000> 2**14 > 10000
     8 30000> 2**15 > 40000
     9 60000> 2**16 > 70000
    10 */
    11 int N[10007];
    12 
    13 int mx[10007][15];
    14 
    15 int main()
    16 {
    17     int n,q;
    18     scanf("%d", &n);
    19     for(int i=0;i<n;i++)
    20     {
    21         scanf("%d", &N[i]);
    22     }
    23     // -------------------------------
    24 
    25     memset(mx, 0, sizeof(mx));
    26     for(int i=0;i<n;i++)
    27     {
    28         mx[i][0] = N[i];
    29     }
    30 
    31     for(int i=1;i<15;i++)
    32     {
    33         for(int j=0;j+(1<<i)-1<n;j++)
    34         {
    35             mx[j][i] = max(mx[j][i-1], mx[j+(1<<i-1)][i-1]);
    36         }
    37     }
    38 
    39     // -------------------------------
    40     scanf("%d", &q);
    41     int lo, hi;
    42     for(int i=0;i<q;i++)
    43     {
    44         scanf("%d%d", &lo, &hi);
    45         int k = (int)log2((double)(hi-lo+1));
    46         int ans = max(mx[lo][k], mx[hi-(1<<k)+1][k]);
    47         printf("%d
    ", ans);
    48     }
    49 }

    当然也可以用线段树解决

  • 相关阅读:
    spring 源码解析一(bean定义)
    IOC 容器中添加组件的方式
    spring 后置处理器
    .NetCore3.1 配置Kestrel端口的几种方式及优先级
    CESIUM空间中AB两点A绕B点的地面法向量旋转任意角度后新的A点坐标(A’)
    Cesium坐标转换:根据两个坐标点(坐标点a、坐标点b)的经纬度,计算a点和b点的角度
    任意一个点A(x,y)围绕任意一个点B(a,b)旋转任意角度后的坐标值
    已知地球上的2点坐标,A和B,求A,B线上 任意点位置。
    centos6下mysql5.7.13制作rpm包
    sql 删除狐立帐户
  • 原文地址:https://www.cnblogs.com/liwenchi/p/8424103.html
Copyright © 2011-2022 走看看