加深对ST算法的理解(Sparse Table)

版权声明:个人做题总结专用~ https://blog.csdn.net/tb_youth/article/details/89684945

Sparse Table(稀疏表):简称ST
ST 算法是基于动态规划的算法,所以它本质就是动态规划。
它的时间复杂度为:O(nlogn)-O(q)。
它 适宜用 于数据不再作出变化 的区间最值 查询问题。
现在假设有一组数据,要求[l,r]内的最小值。
首先我们知道:
对于区间[l,r]:它的区间长度len = r-l+1;
下面将ST的基本思想:
它是基于动态规划,最佳状态为:dp[i][j]
dp[i][j]表示从i开始 区间长度为2^j 的一个区间(即[i,(2^j)-1]) 的最值

(r - l + 1 = 2^j, l = i,  ---->  r = i+(2^j)+1)

对于区间[l,r],可以将它分为两个区间,即二分为两个均匀的区间:
[l,r] ---->[l,r’]+[l’,r],所以这两个区间的区间长度为len/2,
如果用dp[i][j]表示[l,r]这个区间的最小值,
那么对于dp[i][j] = min(dp[i][j-1],dp[i+2^(j-1)][j-1]),这就是状态转移方程
对它的理解:
区间长度减半:(2^j)/2 = 2^(j-1),所以两个子区间的第二维为j-1
dp[i+2^(j-1)][j-1]):

r - l' + 1 = 2^(j-1),r = i + 2^j -1  ----> 
 l' = i+2^j -1+1-2^(j-1)   -------> l' = i + 2^(j-1)

边界:

dp[i][0] = a[i]  (i = 1...n)

写出代码:

for(int i = 1; i <= n; i++)
  {
      cin>>dp[i][0];
  }
  for(int j = 1; 1<<j <= n; j++)
  {
      for(int i = 1; i+(1<<j)-1<=n; i++)
      {
          dp[i][j] = min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
      }
  }

对于i,一个区间[l,r],

dp[i][j],l = i,r-i +1 =  2^j----->  r = i + (1<<j)-1,r <= n,
所以:i+(1<<j)-1 <= n

区间查询:
[l,r]查询最小值,
令 k 为满足 2 ^k <= r-l+1最大整数
则对于区间[l,r]被 以l开头并且区间长度为2^k,和 以r结尾并且区间长度为2^k
的两个区间覆盖

k = log2(r-l+1)

那么
ans = min(dp[l][1<<k],dp[r-(1<<k)+1][k])
l’是用r来表示:

 r - l' + 1 = 1 << k    --->  l' = r - (1<<k) + 1

区间查询代码可写成:

int rmq(int L, int R)
     {
       //int k = 0;
       //while(1<<(k+1) <= R-L+1) k++;
       int k = log2(r-l+1);
       return  min(dp[l][1<<k],dp[r-(1<<k)+1][k]);
     }

题目:
https://www.luogu.org/problemnew/show/P1816
代码:
(预处理k)

#include <iostream>
#define MAXM 100005
#define N 30
#define LL long long
using namespace std;
LL dp[MAXM][N];
LL lg[MAXM];//k = lg[r-l+1]
int main()
{
  int m,n;
  cin>>m>>n;
  lg[0] = -1;
  for(int i = 1; i <= m; i++)
  {
      cin>>dp[i][0];
      lg[i] = lg[i>>1] + 1;
  }
  for(int j = 1; 1<<j <= m; j++)
  {
      for(int i = 1; i+(1<<j)-1<=m; i++)
      {
          dp[i][j] = min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
      }
  }
  int flag = 1;
  while(n--)
  {
      int l,r;
      cin>>l>>r;
      int k = lg[r-l+1];
      if(flag) 
      {
          cout<<min(dp[l][k],dp[r-(1<<k)+1][k]);
          flag = 0;
      }
      else
      cout<<" "<<min(dp[l][k],dp[r-(1<<k)+1][k]);
  }
  cout<<endl;
  return 0;
}

猜你喜欢

转载自blog.csdn.net/tb_youth/article/details/89684945