RMQ问题:高效离线算法——ST

ST算法的实现

ST算法是一种解决静态询问区间最值问题的高效离线算法,算法上采用了倍增的思想,而实现上则是利用了动态规划,具体流程如下所示:

对于区间[begin , end],将其拆分为两个区间:[begin , begin+2^{x}-1]、[end-2^{x}+1 , end],x=floor(log_{2}(end-begin+1)),每个区间的长度为2^{x}。然后继续拆分这两个区间,此时区间的长度为2^{x},此后每次拆分都将区间缩短一半(x-1),不断拆分直到区间长度为1(即x=0)为止。

设f[i][j]表示区间[i , i+2^{j}-1]内的最值,则它拆分后的两个区间分别为:[i , i+2^{j-1}-1]、[i+2^{j-1} , i+2^{j}-1],即f[i][j-1]、f[i+2^{j-1}][j-1]。由此,我们推出求区间最值的状态转移方程为:

                         F[ begin ][ x ]= max/min { F[ begin ][ x-1 ] , F[ begin+( 1<<x-1 ) ][ x-1 ] }

而再加上第一次的拆分,就能得到区间[begin , end]的最值为:

                         Ans=max/min { F[ begin ][ x ] , F[ end-(1<<x)+1 ][ x ] }

我们已经得知每个区间x的值为区间长度取对数,可以直接调用cmath库中的对数函数,但是效率不高,而为了提升计算的效率,我们进行一个预处理,通过递推的方式求得1~length(区间长度)各自对应的x值:

        一个简单推导:      log_{2}d=log_{2}\frac{d}{2}*2=log_{2}\frac{d}{2}+log_{2}2=log_{2}\frac{d}{2}+1,初值为log_{2}1=0

以上,就是st算法的全部内容了,下面来看一个简单例子,有如下一组数据,用st算法计算其最大值:

                                                0     1      2      3      4      5      6     7      8      9      10      11

我们得到其区间长度为:12 ,首先预处理求出所有的x值:

1-0、 2-1、 3-1、 4-2、 5-2、 6-2、 7-2、 8-3、 9-3、 10-3、 11-3、 12-3 

它能拆分出的最长的两个长度为2^{x}的区间为:[0 , 7]  [4 , 11],要求区间[0 , 11]的最大值,则只要求max{ [0 , 7] , [4 , 11] },所以最终答案即为:    max{F[1][3] , f[12-(1<<3)+1][3]}.

然后继续对[0 , 7]  [4 , 11]进行逐渐拆分,直到2^{x}=1,求出每一个区间对应的极值:

                                                                        

每行依次返回最大值:

                                              

同理另一区间的返回值为11,所以得到最终的答案为max{7 , 11}=11.

示例代码

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100005, x = 25;
int num[N],Log[N];
int F[N][x]; //F的含义是从N到N+2^x-1(长度为2^x)的范围内的最大值/最小值
int main() {
	int n, t;
	scanf("%d%d",&n,&t);
	for (int i = 1; i <= n; ++i)
		scanf("%d",&num[i]);
	Log[0] = -1;
	for (int i = 1; i <= n; ++i) 
		F[i][0] = num[i], Log[i] = Log[i >> 1] + 1;
	//递推顺序一定是先j后i
	for (int j = 1; j <= x; ++j)
		for (int i = 1; i + (1 << j) <= n + 1; ++i) //j实际上只用取到floor(log[n]);
			F[i][j] = max(F[i][j - 1], F[i + (1 << j - 1)][j - 1]);//end=i+2^j-1 —> end<=n
	while (t--) {
		int a, b, ans = 0;
		scanf("%d%d",&a,&b);
		int X = Log[b - a + 1];
		ans = max(F[a][X], F[b - (1 << X) + 1][X]);
		printf("%d\n",ans);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值