RMQ问题+ST算法
一、相关定义
RMQ问题
- 求给定区间的最值;
- 一般题目给定许多询问区间。
常见问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。
解决方法
- 暴力搜索 O(n)-O(n)
- 线段树 O(n)-O(q*logn)
- ST算法 O(n*logn)-O(1)
二、ST(Sparse Table)算法
本节介绍了一种比较高效的在线算法(ST算法)解决RMQ问题。
ST算法
- 是一个非常有名的在线处理RMQ问题的算法;
- 基于DP和位运算符;
- 主要过程为:预处理+查询;
- 时间复杂度:在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。
数据说明:
- dp[i][j] 表示从第i位开始,到第i+2j-1位的最大/小值;(即从第i个数起连续2j个数中的最大/小值)
求dp[i][j]的时候,可以把它分成两部分,第一部分从 i 到 i+2j-1-1,第二部分从 i+2j-1 到 i + 2j - 1 。显然二进制数后一个是前一个的两倍,那么可以把区间[i,i+2j] 通过2j-1分成相等的两部分(2j个数,则一定可以二等分),dp[i][j]就是这两段各自最大值中的最大值。 那么转移方程很容易就写出来了。
- mm[i][j] = max( mm[ i ][ j - 1] , mm[ i + (1 << ( j - 1))][ j - 1] )
DP三要素:
- 初始值:dp[i][0] = A[i]
- 状态:dp[i][j] 表示从第i位开始,到第i+2j-1位的最大/小值;
- 状态转移方程:mm[i][j] = max( mm[ i ][ j - 1] , mm[ i + (1 << ( j - 1))][ j - 1] )
三、算法模板
【预处理】——DP(动态规划)
模板代码(以求最大值为例):
void rmq_init(int n)
{
for(int i=1;i<=n;i++)
mm[i][0]=mi[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++) //注意循环的顺序,外层是j,内层是i
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
mm[i][j]=max(mm[i][j-1],mm[i+(1<<(j-1))][j-1]);
}
}
}
这里我们需要注意的是循环的顺序,我们发现外层是j,内层是i,这是为什么呢?可以是i在外,j在内吗?
答案是不可以。因为我们需要理解这个状态转移方程的意义。不懂,请点击。
【查询】——O(1)
查询的时候对于任意一个区间 l -- r ,我们同样可以得到区间长度 len = (r - l + 1)。
那么我们这里用满足不等式“2k<=len”的 k 把区间分成可以交叉的两部分:①[l,l+2k-1];②[r -2k+1,r] 。
例子:查询5,6,7,8,9,我们可以查询5678和6789)。
因为这个区间的长度为 r - l + 1,所以我们可以取k=log2( r - l + 1),则有:RMQ(A, l, r)=max{mm[l , k], mm[ r - 2k + 1, k]}。
举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2] , F[ - 22 + 1, 2]) = max(F[2, 2] , F[5, 2]);
在这里我们也需要注意一个地方,就是<<运算符比+/-运算符的优先级低。
代码:
int rmq(int l,int r)
{
int k=0;
while((1<<(k+1))<=r-l+1)
k++;
//printf("%d %d %d %d\n",l,l+(1<<k),r-(1<<k)+1,r-(1<<k)+1+(1<<k));
int ans1=max(mm[l][k],mm[r-(1<<k)+1][k]);
int ans2=min(mm[l][k],mm[r-(1<<k)+1][k]);
return ans1-ans2; //返回区间最大值与最小值的差
}
四、沙场练兵
题目:士兵杀敌(三)
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std; const int N = 100010;
int maxsum[N][20], minsum[N][20]; void RMQ(int num) //预处理->O(nlogn)
{
for(int j = 1; j < 20; ++j)
for(int i = 1; i <= num; ++i)
if(i + (1 << j) - 1 <= num)
{
maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]);
minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
}
} int main()
{
int num, query;
int src, des;
scanf("%d %d", &num, &query);
for(int i = 1; i <= num; ++i) //输入信息处理
{
scanf("%d", &maxsum[i][0]);
minsum[i][0] = maxsum[i][0];
}
RMQ(num);
while(query--) //O(1)查询
{
scanf("%d %d", &src, &des);
int k = (int)(log(des - src + 1.0) / log(2.0));
int maxres = max(maxsum[src][k], maxsum[des - (1 << k) + 1][k]);
int minres = min(minsum[src][k], minsum[des - (1 << k) + 1][k]);
printf("%d\n", maxres - minres);
}
return 0;
}
优化后代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std; const int N = 100010;
int maxsum[20][N], minsum[20][N]; //优化1 void RMQ(int num) //预处理->O(nlogn)
{
for(int i = 1; i != 20; ++i)
for(int j = 1; j <= num; ++j)
if(j + (1 << i) - 1 <= num)
{
maxsum[i][j] = max(maxsum[i - 1][j], maxsum[i - 1][j + (1 << i >> 1)]); //优化2
minsum[i][j] = min(minsum[i - 1][j], minsum[i - 1][j + (1 << i >> 1)]);
}
} int main()
{
int num, query;
int src, des;
scanf("%d %d", &num, &query);
for(int i = 1; i <= num; ++i) //输入信息处理
{
scanf("%d", &maxsum[0][i]);
minsum[0][i] = maxsum[0][i];
}
RMQ(num);
while(query--) //O(1)查询
{
scanf("%d %d", &src, &des);
int k = (int)(log(des - src + 1.0) / log(2.0));
int maxres = max(maxsum[k][src], maxsum[k][des - (1 << k) + 1]);
int minres = min(minsum[k][src], minsum[k][des - (1 << k) + 1]);
printf("%d\n", maxres - minres);
}
return 0;
}
优化1:数组由F[N][20]变为F[20][N]
因为数组的地址为a + i + j,对应上面数组,我们需要先循环N的部分,所以
如果是第一种,我们计算时因为i不断变化,我们就需要计算a + i + j
如果是第二种,我们计算时a + i不变,只需要改变j
优化2:位运算
RMQ问题+ST算法的更多相关文章
- RMQ的ST算法
·RMQ的ST算法 状态设计: F[i, j]表示从第i个数起连续2^j个数中的最大值 状态转移方程(二进制思想): F[i, j]=max(F[i,j-1], ...
- RMQ(ST算法)
RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列a,回答若干询问RMQ(A,i,j)(i, j<=n),返回数列a中下标在i ...
- RMQ之ST算法模板
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; ; ],M ...
- RMQ问题——ST算法
比赛当中,常会出现RMQ问题,即求区间最大(小)值.我们该怎样解决呢? 主要方法有线段树.ST.树状数组.splay. 例题 题目描述 2008年9月25日21点10分,酒泉卫星发射中心指控大厅里,随 ...
- [总结]RMQ问题&ST算法
目录 一.ST算法 二.ST算法の具体实现 1. 初始化 2. 求出ST表 3. 询问 三.例题 例1:P3865 [模板]ST表 例2:P2880 [USACO07JAN]平衡的阵容Balanced ...
- RMQ问题ST算法 (还需要进一步完善)
/* RMQ(Range Minimum/Maximum Query)问题: RMQ问题是求给定区间中的最值问题.当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的 ...
- RMQ 问题 ST 算法(模板)
解决区间查询最大值最小值的问题 用 $O(N * logN)$ 的复杂度预处理 查询的时候只要 $O(1)$ 的时间 这个算法是 real 小清新了 有一个长度为 N 的数组进行 M 次查询 可 ...
- RMQ之ST算法
#include <stdio.h> #include <string.h> ; int a[N]; ]; inline int min(const int &a, c ...
- Round #4 RMQ问题ST算法
前几天群里看到有人问[JSOI2008]最大数,一道很简单的问题,线段树无脑做,但是看到了动态ST,emmm,学学吧,听大佬说了下思路,还好,不难的: 四道题都可以用其他数据结构或做法代替,例如线段树 ...
随机推荐
- 20.springboot项目部署到linux服务器文件上传临时路径处理问题
1.前言 把项目部署到服务器上之后,文件上传默认会在/tmp路径中. 之前想了各种解决办法,比如如何更改这个上传路径...... 最后发现不是个好的方法,当然就想到了更好的解决方案. 就是我把上传文件 ...
- Qt数据库编程1
Qt中数据编程主要分为以下两点:1.利用qt提供类 访问数据库或者成为简单的数据库编程2.数据库编程中引入model/view编程模型 qt中数据库编程的步骤: 1.加载数据库驱动 QSqlDatab ...
- Luogu P2590 [ZJOI2008]树的统计
最近在学树剖,看到了这题就做了 [ZJOI2008]树的统计 思路 从题面可以知道,这题是树剖题(要求的和模板没什么区别呀喂 就是在普通的树剖上加了一个最大值 所以可以知道就是树剖+特殊的线段树 线段 ...
- mysql在cmd里中文乱码解决办法
右边画红线部分中文已经乱码,左边红线里中文则完美显示出来了. 解决办法 用set names utf-8: 效果如图
- PHP基础 (麦子学院 第二阶段)
zendstudio 10.0破解版,新建完项目后,首先修改项目的编码方式,统一改成utf-8 (选中项目,再右键properties:Text file encoding).修改字体大小. apac ...
- 用友二次开发之科脉TOT3凭证接口
按客户的要求,根据科脉导出的数据,开发一个工具,将凭证导入T3 这个科目导出的凭证格式. 选择账套登陆,你没看错,这个是我开发的登陆界面. 选择接口文件. 软件自动进数据分类,你可以看到数据了.但只是 ...
- Python协程中使用上下文
在Python 3.7中,asyncio 协程加入了对上下文的支持.使用上下文就可以在一些场景下隐式地传递变量,比如数据库连接session等,而不需要在所有方法调用显示地传递这些变量.使用得当的话, ...
- windows和Ubuntu下安装mongodb
windows 下载 mongodb官网下载压缩版安装包:下载地址:https://www.mongodb.com/download-center/community 注意选择版本(目前windows ...
- Make命令完全详解教程
Make命令完全详解教程 无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令.不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或make install.利用m ...
- vue---day02
1. 全局组件的注册 - 创建根实例的时候,data可以是object,也可以是函数 - 创建组件的时候,data必须是函数 1.1 创建 Vue.component('global-componen ...