RMQ问题——ST算法

  1. 什么是RMQ、ST:RMQ(Range Minimum/Maximum Query)问题,即求区间的最值。可以写一个线段树来实现,但是每次查询的时间复杂度为O(log n),若查询次数过多则可能超时。ST算法是一种离线算法,经过O(nlogn)的预处理后,可以在O(1)的时间复杂度内进行查询,缺点是无法对数据做出修改。
  2. 算法实现:

     

    初始化:用dp实现初始化。a[]为原始数据数组f,[i][j]表示从i向后的2j个数字中的最值。显然f[i][0]=a[i];

    我们将f[i][j]分为两段,一段为a[i]~a[2j-1]的最值即f[i][j-1],一段为a[i+2j-1]~a[i+2j]即f[i+1<<(j-1)][j-1];这样就得到了状态转移方程f[i][j]=max/min(f[i][j-1],f[i+1<<(j-1)][j-1]);。

     dp数组即f数组;

     1         for(i=1;i<=n;i++){
     2             dpmax[i][0]=a[i];
     3             dpmin[i][0]=a[i];//初始化
     4         };
     5         int end_j=log(n+0.0)/log(2.0);//计算j的最大值
     6         int endi;
     7         for(j=1;j<=end_j;j++){//注意,由于每一个dp[i,j]的求解都要用到dp[i,j-1]故j应放在外层循环
     8             endi=n+1-(1<<j);//计算i的最大值
     9             for(i=1;i<=endi;i++){
    10                 dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);
    11                 dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);//dp预处理
    12             }
    13         }

     

    查询:得到f数组之后,若要查询[l,r]的最值,则将区间分成两段,l~log2r与r-log2r~r,则两段的最值中较大(小)的即为答案。

     

    1         for(i=1;i<=m;i++){
    2             scanf("%d%d",&l,&r);
    3             k=log(r-l+1.0)/log(2.0);//计算分割点
    4             printf("%d\n",max(dpmax[l][k],dpmax[r-(1<<k)+1][k])//分两段查询
    5                     -min(dpmin[l][k],dpmin[r-(1<<k)+1][k]));
    6         }

     

  3. 例题:POJ3264

    题目大意:给出一串的数字,然后给出一个区间a b,输出从ab的最大的数和最小的数的差。

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstring>
     4 #include<cstdio>
     5 using namespace std;
     6 
     7 int a[100000],dpmax[50000][100],dpmin[50000][100];
     8 
     9 int main(){
    10     std::ios::sync_with_stdio(false);
    11     int i,j,m,n,k,t,l,r;
    12     while(scanf("%d%d",&n,&m)!=EOF){
    13         for(i=1;i<=n;i++)scanf("%d",&a[i]);
    14         for(i=1;i<=n;i++){
    15             dpmax[i][0]=a[i];
    16             dpmin[i][0]=a[i];//初始化
    17         };
    18         int end_j=log(n+0.0)/log(2.0);//计算j的最大值
    19         int endi;
    20         for(j=1;j<=end_j;j++){//注意,由于每一个dp[i,j]的求解都要用到dp[i,j-1]故j应放在外层循环
    21             endi=n+1-(1<<j);//计算i的最大值
    22             for(i=1;i<=endi;i++){
    23                 dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]);
    24                 dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]);//dp预处理
    25             }
    26         }
    27         for(i=1;i<=m;i++){
    28             scanf("%d%d",&l,&r);
    29             k=log(r-l+1.0)/log(2.0);//计算分割点
    30             printf("%d\n",max(dpmax[l][k],dpmax[r-(1<<k)+1][k])//分两段查询
    31                     -min(dpmin[l][k],dpmin[r-(1<<k)+1][k]));
    32         }
    33         return 0;
    34     }
    35 }

     

     

     

posted @ 2016-07-29 22:16  羊毛羊  阅读(341)  评论(0编辑  收藏  举报