版权声明:个人做题总结专用~ 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;
}