ST算法(Sparse Table)

ST算法能够解决什么问题?

时间复杂度O(nlogn)预处理、O(1)查询区间最大值、最小值等。

ST算法讲解:

该算法采用二进制倍增的思想,二进制倍增就是1,2,4,8,16...

比如现在有8个数,求min:
2 3 4 5 6 4 3 2

现在你要查询的区间是[l,r]
如果l==r就直接可以查询该位置数即可,但是查询[1,4],[3,8]这类呢?
假如问题换成q<=10^5次查询,每次查询肯定要时间复杂度O(1),才不会超时,
O(1)情况下,肯定是要打表了!!
然后打个dp[i][j]表~ 
  下标:0  1  2  3  j   <---倍增在这里!!
    ------------------
    1|  2  2  2  2
    2|  3  3  3  0
    3|  4  4  4  0
    4|  5  5  3  0
    5|  6  4  2  0
    6|  4  3  0  0
    7|  3  2  0  0
    8|  2  0  0  0
    i|

0: 会发现dp[i][0]这一列是原数组
1: dp[i][1]这列怎么来的?
   dp[i][1]=min(dp[i][j-1],dp[i+1][j-1])
   比如:dp[4][1]=4 --> min(dp[4][0],dp[5][0])
2: dp[i][2]=min(dp[i][j-1],dp[i+2][j-1])
   比如:dp[4][2]=3 --> min(dp[4][1],dp[6][1])
   想想转移方程为什么这样写?            
        
                                      / dp[4][0]
                    先取上一列dp[4][1]
                   /                  \ dp[5][0]
   当前点为dp[4][2]
                   \                  / dp[6][0]
                    再去上一列dp[6][1]
                                      \ dp[7][0]
    发现了什么?

    dp[4][1]所代表的为下标(4,5)的最小值。
    dp[4][2]所代表的为下标(4,5,6,7)的最小值。
同理dp[1][3]所代表的为下标(1,2,3,4,5,6,7,8)的最小值。
    
    j=0时,区间为本身。
    j=1时,区间为相邻2个数。
    j=2时,区间为相邻4个数。
    j=3时,区间为相邻8个数。
    .....
   ##**  这是不是倍增? **##

普及知识点:
log以2为底的8=3  -->log(8)/log(2)=3 (log的换底公式)

1.查询区间[2,3]
    区间共两个数,k=(int)((log(r-l+1.0)/log(2.0)))=1;
    查询min(dp[2][k],dp[2][k]);
2.查询区间[2,4]
    区间共三个数,k=1;
    查询min(dp[2][1],dp[3][1]);
    (dp[2][1]是区间(2,3)的最小值,dp[3][1]是区间(3,4)的最小值,所以合并查询的是区间[2,3,4])
3.查询区间[2,5]
    区间共四个数,k=2;
    查询min(dp[2][2],dp[2][2]);
    (dp[2][2]是区间(2,3,4,5)的最小值,合并区间[2,3,4,5])
4.查询区间[2,7]
    区间共六个数,k=2;
    查询min(dp[2][2],dp[4][2]);
    (dp[2][2]区间(2,3,4,5),dp[3][2]区间(4,5,6,7),合并区间(2,3,4,5,6,7))
5.查询区间[1,8]
    区间共八个数,k=3;
    查询min(dp[1][3],dp[1][3]);
    .....合并区间(1,2,3,4,5,6,7,8)
#include <iostream>
#include <cmath>
using namespace std;
int n;
int a[100005];
int dp[100005][17];
void ST()
{
	//n为数组个数
	//bitn为n的二进制位数
	int bitn=log(n)/log(2)+1;
	for(int i=1; i<=n;i++)
   		dp[i][0]=a[i];
   		
	for(int j=1;j<bitn;j++)
   	for(int i=1;i<=n;i++)
   	{
       	if(i+(1<<(j-1))>n) break;
       	dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
}
int query(int l,int r)  //查询区间[s,e]的最值
{
    int k=(int)((log(r-l+1.0)/log(2.0)));
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}

/*
2 3 4 5 6 4 3 2

2 2 2 2
3 3 3 0
4 4 4 0
5 5 3 0
6 4 2 0
4 3 0 0
3 2 0 0
2 0 0 0
*/ 
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
发布了39 篇原创文章 · 获赞 27 · 访问量 4124

猜你喜欢

转载自blog.csdn.net/qq_43381887/article/details/100592543
今日推荐