zoukankan      html  css  js  c++  java
  • RMQ算法 (ST算法)

     概述:

      RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。对于一次查询,可以暴力地O(n),但是当查询次数很多的时候,这样的暴力就无法进行了。这时我们可以通过RMQ算法来解决这个问题。

    RMQ(ST):(关于学习RMQ的博客:框架即讲解比较详细具体代码比较好

      ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。

      首先是预处理,用动态规划(DP)解决。设A[i]是要求区间最值的数列,F[i, j]表示从第i个数起连续2^j个数中的最大值。例如数列3 2 4 5 6 8 1 2 9 7,F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。 F[1,2]=5,F[1,3]=8,F[2,0]=2,F[2,1]=4……从这里可以看出F[i,0]其实就等于A[i]。这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。我们把F[i,j]平均分成两段(因为f[i,j]一定是偶数个数字),从i到i+2^(j-1)-1为一段,i+2^(j-1)到i+2^j-1为一段(长度都为2^(j-1))。用上例说明,当i=1,j=3时就是3,2,4,5 和 6,8,1,2这两段。F[i,j]就是这两段的最大值中的最大值。于是我们得到了动态规划方程F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])。

    然后是查询。取k=[log2(j-i+1)],则有:RMQ(A, i, j)=min{F[i,k],F[j-2^k+1,k]}。 举例说明,要求区间[2,8]的最大值,就要把它分成[2,5]和[5,8]两个区间,因为这两个区间的最大值我们可以直接由f[2,2]和f[5,2]得到。

     1 int vec[MAX_N];
     2 int dp[MAX_N][25];
     3 void ST(int N)
     4 {
     5     for(int i=1;i<=N;i++) dp[i][0] = vec[i];
     6     for(int j=1;(1<<j) <= N;j++)
     7     {
     8         for(int i=1;i+(1<<j)-1<=N;i++)
     9         {
    10             dp1[i][j] = max(dp[i][j-1],dp[i+(1<<j-1)][j-1]); //由于移位操作的优先度低,1<<j-1 = 1<<(j-1);
    11         }
    12     }
    13 }
    14 int RMQ(int l,int r)
    15 {
    16     int k = 0;
    17     while((1<<k+1) <= r-l+1) k++;
    18     return max(dp1[l][k],dp1[r-(1<<k)+1][k]);
    19 }

    POJ-2364

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 const int MAX_N = 5e4+9;
     6 const int INF = 1e9+7;
     7 int vec[MAX_N];
     8 int dp1[MAX_N][25];
     9 int dp2[MAX_N][25];
    10 void ST(int N)
    11 {
    12     for(int i=1;i<=N;i++) dp1[i][0] = dp2[i][0] = vec[i];
    13     for(int j=1;(1<<j)<=N;j++)
    14     {
    15         for(int i=1;i+(1<<j)-1 <= N;i++)
    16         {
    17             dp1[i][j] = max(dp1[i][j-1],dp1[i+(1<<j-1)][j-1]);
    18             dp2[i][j] = min(dp2[i][j-1],dp2[i+(1<<j-1)][j-1]);
    19         }
    20     }
    21 }
    22 int RMQ(int l,int r)
    23 {
    24     int k = 0;
    25     while((1<<k+1) <= r-l+1) k++;
    26     return max(dp1[l][k],dp1[r-(1<<k)+1][k]) - min(dp2[l][k],dp2[r-(1<<k)+1][k]);
    27 }
    28 int main()
    29 {
    30     int N,M,T;
    31     while(cin>>N>>M)
    32     {
    33         for(int i=1;i<=N;i++)
    34         {
    35             scanf("%d",&vec[i]);
    36         }
    37         ST(N);
    38         for(int i=0;i<M;i++)
    39         {
    40             int l,r;
    41             scanf("%d%d",&l,&r);
    42             int ans = RMQ(l,r);
    43             printf("%d
    ",ans);
    44         }
    45     }
    46     return 0;
    47 }
    View Code
  • 相关阅读:
    Just oj 2018 C语言程序设计竞赛(高级组)D: 四边形面积
    Just Oj 2017C语言程序设计竞赛高级组A: 求近似值(矩阵快速幂)
    HDU 1166 敌兵布阵(线段树/树状数组模板题)
    HDU 1541 STAR(树状数组)
    Just Oj 2017C语言程序设计竞赛高级组E: DATE ALIVE(二分匹配)
    Just Oj 2017C语言程序设计竞赛高级组D: 字符串最大表示(next数组)
    蓝桥杯 历届试题 小计算器
    蓝桥杯练习 十六进制转二进制
    51 nod 1212 无向图最小生成树(Kruckal算法/Prime算法图解)
    51 Nod 1240 莫比乌斯函数
  • 原文地址:https://www.cnblogs.com/doggod/p/8375923.html
Copyright © 2011-2022 走看看