RMQ之ST表维护区间极值

本文介绍了一种基于预处理的区间最小值查询算法,通过构建二维数组存储子区间的最小值,实现快速查询任意区间内的最小值。文章详细解释了算法原理及其实现过程,并给出了具体的初始化与查询操作的伪代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

令d[i][j]表示从i开始,长度为2^j的一段元素中的最小值,用递推方式计算,d[i][j]=min(d[i][j-1],d[i+2^j-1,j-1),就是将[i,i+2^j]的这段区间一分为二,然后取两段区间的极值就可以了。

2^j小于等于n,所以一般来讲数组第二维开30就够了。

至于递推的计算顺序,j总是与前一项j-1有关,所以对j的循环放在外层,就不会破坏计算顺序。

查询操作就是,令k为满足2^k<=R-L+1的最大整数,则以L,R开头和结尾的两个长度为2^k的区间合并覆盖了L和R的区间


代码的下标从0开始

void RMQ_init(){
    for(int i=0;i<n;i++) d[i][0]=a[i];
    for(int j=1;(1<<j)<=n;j++){
        for(int i=0;i+(1<<j)-1<n;i++){
            d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);
        }
    }
}

int RMQ(int L,int R){
    int k=0;
    while((1<<(k+1))<=R-L+1) k++;
    return max(d[L][k],d[R-(1<<k)+1][k]);
}



/* translation: 给出一列数列,按照不递减的顺序。并且给出若干查询操作,查询某一区间出现频率最频繁的次数是多少? solution: RMQST算法 首先设置数组dp[i][j]表示从i开始长度为2^i次方的查询结果。则根据动态规划有: dp[i][j] = max(dp[i][j-1], dp[i+(len >> 1)][j-1])。 然后根据数据不递减的特点,可以将其“游标编程”。并且在查询时对两端的点进行单独判断。然后按照ST的 查询操作即可。 note: *: 习得ST算法解决RMQ的正确姿势ORZ date: 2016.11.16 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 100000 + 5; int n, q; int a[maxn]; int pos[maxn], p; int Left[maxn], Right[maxn]; int dp[maxn][50]; void RMQ_init() { memset(dp, 0, sizeof(dp)); for(int i = 0; i <= p; i++) dp[i][0] = Right[i] - Left[i] + 1; for(int j = 1; (1 << j) <= p; j++){ int len = 1 << j; for(int i = 0; i + len - 1 <= p; i++) dp[i][j] = max(dp[i][j-1], dp[i+(len >> 1)][j-1]); } } int query(int L, int R) { int k = 0; while((1 << (k+1)) <= R - L + 1) k++; return max(dp[L][k], dp[R-(1<<k)+1][k]); } int main() { //freopen("in.txt", "r", stdin); while(~scanf("%d%d", &n, &q) && n){ memset(Left, 0, sizeof(Left)); memset(Right, 0, sizeof(Right)); memset(pos, 0, sizeof(pos)); a[0] = maxn * 10; p = 0; for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); if(a[i] != a[i-1]){ p++; pos[i] = p; Left[p] = Right[p] = i; }else{ pos[i] = p; Right[p]++; } } RMQ_init(); int from, to; while(q--){ scanf("%d%d", &from, &to); int ans; if(pos[from] == pos[to]) ans = to - from + 1; else if(pos[to] - pos[from] == 1){ ans = max(Right[pos[from]] - from + 1, to - Left[pos[to]] + 1); }else{ ans = max(Right[pos[from]] - from + 1, to - Left[pos[to]] + 1); ans = max(query(pos[from] + 1, pos[to] - 1), ans); } printf("%d\n", ans); } } return 0; }分模块讲解
最新发布
03-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值