RMQ(Range Minimum Query)算法
2015-09-18 16:35
423 查看
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
主要方法及复杂度如下:
1、朴素(即搜索),O(n)-O(qn) online。
2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(q) online。
首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。
这里主要介绍一个ST算法。
用F[i,j]表示从第i个数开始连续2^j个数的最值!!!(注意不是到第2^j个数)
那么,可知每个区间有偶数个数(除了2^0),将这些区间分成两个区间,i到2^(j-1)-1,和i+2^(j-1)到2^j-1,所以可得动态方程为F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]);
初始状态为F[i,0],即,从第i个数开始连续1个数的最值,也就是第i个数本身。
这里我们需要注意的是循环的顺序,我们发现外层是j,内层所i,这是为什么呢?可以是i在外,j在内吗?
答案是不可以。因为我们需要理解这个状态转移方程的意义。
状态转移方程的含义是:先更新所有长度为F[i,0]即1个元素,然后通过2个1个元素的最值,获得所有长度为F[i,1]即2个元素的最值,然后再通过2个2个元素的最值,获得所有长度为F[i,2]即4个元素的最值,以此类推更新所有长度的最值。
而如果是i在外,j在内的话,我们更新的顺序就是F[1,0],F[1,1],F[1,2],F[1,3],表示更新从1开始1个元素,2个元素,4个元素,8个元素(A[0],A[1],....A[7])的最值,这里F[1,3] = max(max(A[0],A[1],A[2],A[3]),max(A[4],A[5],A[6],A[7]))的值,但是我们根本没有计算max(A[0],A[1],A[2],A[3])和max(A[4],A[5],A[6],A[7]),所以这样的方法肯定是错误的。
预处理之后,开始query。
假设查询的区间是(i,j),那么int k=log2(i-j+1)。取最小幂。
举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);
例题(hdu 5443 The water problem):
主要方法及复杂度如下:
1、朴素(即搜索),O(n)-O(qn) online。
2、线段树,O(n)-O(qlogn) online。
3、ST(实质是动态规划),O(nlogn)-O(q) online。
ST算法(Sparse Table),以求最大值为例,设d[i,j]表示[i,i+2^j-1]这个区间内的最大值,那么在询问到[a,b]区间的最大值时答案就是max(d[a,k], d[b-2^k+1,k]),其中k是满足2^k<=b-a+1(即长度)的最大的k,即k=[ln(b-a+1)/ln(2)]。
d的求法可以用动态规划,d[i, j]=max(d[i, j-1],d[i+2^(j-1), j-1])。
4、RMQ标准算法:先规约成LCA(Lowest Common Ancestor),再规约成约束RMQ,O(n)-O(q) online。
首先根据原数列,建立笛卡尔树,从而将问题在线性时间内规约为LCA问题。LCA问题可以在线性时间内规约为约束RMQ,也就是数列中任意两个相邻的数的差都是+1或-1的RMQ问题。约束RMQ有O(n)-O(1)的在线解法,故整个算法的时间复杂度为O(n)-O(1)。
这里主要介绍一个ST算法。
用F[i,j]表示从第i个数开始连续2^j个数的最值!!!(注意不是到第2^j个数)
那么,可知每个区间有偶数个数(除了2^0),将这些区间分成两个区间,i到2^(j-1)-1,和i+2^(j-1)到2^j-1,所以可得动态方程为F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]);
初始状态为F[i,0],即,从第i个数开始连续1个数的最值,也就是第i个数本身。
void RMQ(int num){ for(int j=1;j<32;j++){//int类型最大2^32-1 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]=max(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]); } } } }
这里我们需要注意的是循环的顺序,我们发现外层是j,内层所i,这是为什么呢?可以是i在外,j在内吗?
答案是不可以。因为我们需要理解这个状态转移方程的意义。
状态转移方程的含义是:先更新所有长度为F[i,0]即1个元素,然后通过2个1个元素的最值,获得所有长度为F[i,1]即2个元素的最值,然后再通过2个2个元素的最值,获得所有长度为F[i,2]即4个元素的最值,以此类推更新所有长度的最值。
而如果是i在外,j在内的话,我们更新的顺序就是F[1,0],F[1,1],F[1,2],F[1,3],表示更新从1开始1个元素,2个元素,4个元素,8个元素(A[0],A[1],....A[7])的最值,这里F[1,3] = max(max(A[0],A[1],A[2],A[3]),max(A[4],A[5],A[6],A[7]))的值,但是我们根本没有计算max(A[0],A[1],A[2],A[3])和max(A[4],A[5],A[6],A[7]),所以这样的方法肯定是错误的。
预处理之后,开始query。
假设查询的区间是(i,j),那么int k=log2(i-j+1)。取最小幂。
举例说明,要求区间[2,8]的最大值,k = log2(8 - 2 + 1)= 2,即求max(F[2, 2],F[8 - 2 ^ 2 + 1, 2]) = max(F[2, 2],F[5, 2]);
例题(hdu 5443 The water problem):
/* * mai.cpp * * Created on: 2015年9月15日 * Author: chen */ #include<stdio.h> #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<vector> #include<time.h> #include<queue> #include<stack> #include<iterator> #include<math.h> #include<stdlib.h> #include<limits.h> #include<memory.h> //#define ONLINE_JUDGE #define eps 1e-8 #define INF 0x7fffffff #define FOR(i,a) for((i)=0;i<(a);(i)++) //[i,a); #define MEM(a) (memset((a),0,sizeof(a))) #define sfs(a) scanf("%s",a) #define sf(a) scanf("%d",&a) #define sfI(a) scanf("%I64d",&a) #define pf(a) printf("%d\n",a) #define pfI(a) printf("%I64d\n",a) #define pfs(a) printf("%s\n",a) #define sfd(a,b) scanf("%d%d",&a,&b) #define sft(a,b,c)scanf("%d%d%d",&a,&b,&c) #define for1(i,a,b) for(int i=(a);i<b;i++) #define for2(i,a,b) for(int i=(a);i<=b;i++) #define for3(i,a,b)for(int i=(b);i>=a;i--) #define MEM1(a) memset(a,0,sizeof(a)) #define MEM2(a) memset(a,-1,sizeof(a)) #define LL __int64 const double PI = acos(-1.0); template<class T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; } template<class T> T lcm(T a, T b) { return a / gcd(a, b) * b; } template<class T> inline T Min(T a, T b) { return a < b ? a : b; } template<class T> inline T Max(T a, T b) { return a > b ? a : b; } using namespace std; template<class T> T Mint(T a, T b, T c) { if (a>b) { if (c>b) return b; return c; } if (c>a) return a; return c; } template<class T> T Maxt(T a, T b, T c) { if (a>b) { if (c>a) return c; return a; } else if (c > b) return c; return b; } #define maxn 50 int T,n,q,l,r; int maxsum[1010][maxn]; int minsum[1010][maxn]; void RMQ(int num){ 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]=max(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]); } } } } int main(){ #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif while(~sf(T)){ while(T--){ sf(n); for2(i,1,n){ sf(maxsum[i][0]); } RMQ(n); sf(q); while(q--){ sfd(l,r); int k=(int)(log(r-l+1.0)/log(2.0)); int ans = max(maxsum[l][k], maxsum[r - (1<<k) + 1][k]); pf(ans); } } } return 0; }
相关文章推荐
- iOS UISeachBar键盘消失 虚拟按钮
- UIView(包括子类)的几个初始化时执行动作的时机
- UITableView的使用
- 我的DUILIB常用库
- 使用rpmbuild 进行rpm 打包
- 使用rpmbuild 进行rpm 打包
- request.getParameterValues()
- ScrollView上加Button UIControlEventTouchUpInside事件超出边界扔响应问题
- ERROR ITMS-90475: "Invalid Bundle. iPad Multitasking support requires launch story board in bundle '
- .NET下为百度文本编辑器UEditor增加图片删除功能
- Qt Quik是什么?
- NameValueCollection类
- 如何缩放SpriteBuilder中的scene
- 如何缩放SpriteBuilder中的scene
- 如何缩放SpriteBuilder中的scene
- 第3.3 案例2: 工作队列 job queue
- 文档中导入 UE编辑器
- iOS中给UIButton通过runtime给响应方法传参
- 随记 INIT GET_COUNT INQUIRE NEWINQUIRE STOP EXIT
- 快速排序Quick sort - golang