RMQ谈:Range Minimum/Maximum Query 区间最小/大值查询 ST优化

RMQ(Range Minimum/Maximum Query)  对于长度为n的数列A,回答若干询问RMQ(A, i, j) (i,j<=n),返回数列A中下标在i,j里的最小(大)值

给出m次查询   最常想到的是遍历  复杂度为O(nm).

改进方法:

(1)、ST(sparse table)算法 

基于这样的一个事实:一大块区间求最大/小值,可以分成两个小块分别求出最大/小值(分治法的思想),然后再将 求出的两个极值 比较 ,求出最后最值。

                                                   例如:                                    

a)、预处理

分配一个d[n][log2n]大小的数据区:

d[i][k] 记录数列A[i,  i+2k-1]之间的最值(包括两端点),时间复杂度O(nlog2n)

在做预处理时有一个动态规划的思想:d[i][k] = min/max( d[i][ k-1 ],d[  i+2k-1  ][ k-1 ] ).

b)、处理查询

k=[log2(j-i+1)];

RMQ(i,j) = min/max(d[i][k],d[j-2k+1][k]);

这里有个小技巧是: d[j-2k+1][k]下标j开始往前数2k个数  与      d[i][k]从下标i开始往后数2k个数一定包括了A[i,j]这个区间(个数小于2k+1

时间复杂度O(1)

#include <iostream>
#include <stdlib.h>
#include <math.h>

using namespace std;

void swap(int *i,int *j);
int query(int i,int j);
int **table;

void RMQ(int arr[],int n)
{
  //cout<<"RMQ"<<endl;
  int w = log2(n);
  for(int i=1;i<=n;i++)table[i][0]=arr[i];//cout<<table[i][0]<<endl;
  int tmp;//cout<<w<<endl;
  for(int i=1;i<=w;i++)
  {
     tmp=1;tmp=tmp<<(i-1);
     for(int j=1;j+tmp<=n;j++)        
     {
        if(i==4&&j==1)cout<<*(*(table+j)+i-1)<<" "<<*(*(table+j+tmp)+i-1)<<endl;
        if(*(*(table+j)+i-1) < *(*(table+j+tmp)+i-1))
            *(*(table+j)+i) = *(*(table+j)+i-1);
        else
            *(*(table+j)+i) = *(*(table+j+tmp)+i-1);
     }
  }
}

int main()
{
  int n;
  cin>>n;
  int *arr = (int *)malloc(sizeof(int)*(n+1));
  int *p = arr;
  for(int i=1;i<=n;i++)
  {
     *(p+i) = rand()%17 + 1;
     cout<<*(p+i)<<" ";
  }cout<<endl;
  
  table = (int **)malloc(sizeof(int*)*(n+1));
  for(int i=1;i<=n;i++)*(table+i) = (int *)malloc(sizeof(int)*(log2(n)+1));

  RMQ(arr,n);
  
  for(int j=1,k=1;j<=n;j++)
  {
     int step = 0;cout<<j<<":  ";
     for(int i=0;j+(1<<i)-1<=n;i++)        
     {        
        printf("%5d ",*(*(table+j)+i));
     }
     cout<<endl;
  }
  
  int m;
  cin>>m;
  while(m--)
  {
    int i,j;
    cin>>i>>j;
    if(i>j)swap(i,j); 
    if(i<1)i=1;
    if(j>n)j=n;
    cout<<"result: "<<query(i,j)<<endl;         
  }
  
  for(int i=1;i<=100;i++)
  {
     cout<<*(p+i)<<" "<<endl;
  }
  system("pause");
  return 0;    
}

void swap(int *i,int *j)
{
  int tmp = *i;     
  *i = *j;
  *j = tmp;
}

int query(int i,int j)
{
    int dif = j-i+1;
    int log_dif = log2(dif);
    int tmp = 1;
    //cout<<i<<" "<<j<<" "<<log_dif<<endl;
    if(*(*(table+i)+log_dif) < *(*(table+j-(tmp<<log_dif)+1)+log_dif))
       return *(*(table+i)+log_dif);
    else
       return *(*(table+j-(tmp<<log_dif)+1)+log_dif);
}
原文地址:https://www.cnblogs.com/lwer/p/2833306.html