RMQ算法(一):ST表(Sparse Table)

ST算法是RMQ的一种实现形式,用来求解给定区间的最值,RMQ还可用线段树、splay树,转化为LCA问题等解决,但ST表要快,最常用的就是ST表和线段树,ST表适用于查询特别多的情况。

ST算法基于动态规划和倍增思想,分为两部分,离线预处理和在线查询:

(1)离线预处理

利用动态规划思想求解区间最值,区间在增加时,利用倍增,每次增加2^i个长度。

具体为:f[i,j]表示以i为起点,区间长度为2^j的区间[i,i + 2^j - 1]的最值。

求解f[i,j]时,先把长度为2^j的区间[i,i + 2^j - 1]分成两等份,每份长度均为2^(j - 1),分别求解子区间的最值f[i,j - 1]和f[i + 2^(j - 1),j - 1],最后得到整个区间的最值。

状态转移方程:f[i,j] = max/min(f[i,j - 1],f[i + 2^(j - 1),j - 1]),初始状态为:f[i,0] = a[i]。

(2)在线查询

预处理时,假设了总区间长度为2^i,可是给出的区间长度不一定为2^i,应对总区间进行处理。把总区间分成两个子区间,满足:这两个子区间要能覆盖整个区间,为了利用预处理的结果和运算方便,要求子区间长度相等且都为2^i,所以两个子区间可能出现重叠。

具体实现为:由区间总长度n得k = (int)floor(log(n)/log(2.0)),在线查询的方程为:

re = max/min(f[left][k],f[right - (1<< k)+ 1][k]),这样既利用了预处理结果,又做到了区间元素不遗漏,但子区间会有重叠。

由代码可知,算法复杂度为O(NlogN)- O(1),其间在线查询的复杂度为常数级。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
int n;
int a[100005], dp[100005][20], upper[100005];//以最小值为例;upper[i]实际表示(int)floor(log(n)/log(2.0))
void init() {//这个init()有些费时,可以去掉,改为用到时利用(int)floor(log(n)/log(2.0))计算
	upper[0] = -1;
	for (int i = 1; i <= 100005; i++) {
		upper[i] = ((i & (i - 1)) == 0) ? upper[i - 1] + 1 : upper[i - 1];
	}
}
void rmq_init() {
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]);
		dp[i][0] = a[i];
	}
	int k = upper[n];
	for (int i = 1; i <= k; i++) {
		for (int j = 0; j + (1 << i) - 1 < n; j++) {
			dp[j][i] = min(dp[j][i - 1], dp[j + (1 << (i - 1))][i - 1]);
		}
	}
}
int rmq(int le, int rig) {
	int k = upper[rig - le + 1];
	return min(dp[le][k], dp[rig - (1 << k) + 1][k]);
}
int main() {
	int a, b, q;
	init();
	while (scanf("%d%d", &n,&q) != EOF) {
		rmq_init();
		for (int i = 0; i < q; i++) {
			scanf("%d%d", &a, &b);
			printf("%d\n", rmq(a, b));
		}
	}
	return 0;
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值