标签:++ 技术 阅读 nlog == 长度 ima print 最大的
RMQ这种算法,有好处也有坏处。
好处是代码量比其他算法(线段树,树状数组等)稍短(又是很短),坏处是局限性太大,不如线段树灵活。
它的目的是求区间最值。
我们先看一道简单题。
有一个序列,以及一些操作,每次操作给出一个区间[l,r],求这个区间的最大值。
如果你之前阅读
或其他有关线段树的资料,这题就是小菜一碟。
当我们摆脱线段树,从另一个视角看这题。
我们把它想成一道区间DP,用dp[i][j]表示区间[i,j]的最大值。
显而易见,有dp[i][j]=max(dp[i][j-1],a[i])(时间复杂度O(n^2))
我们采用倍增思想,用dp[i][j]表示以i为起点,长度为2j的区间最大值,求解时分成两个区间[i,i+2j-1-1],[i+2j-1,i+2j+1]
有dp[i][j]=max(dp[i][j-1],dp[i+2j-1][j-1])
由于长度为2j,时间复杂度为O(nlogn)
以上是预处理部分。
实际查询时,我们需要求解一个任意的区间,而上面的预处理只能求出长度为2j的区间最大值。
我们利用一下一个小性质:
我们的目标就是要把待查询的区间,转化成两个我们已经求出的区间。
找出最大的2k<=len的k,其中len=r-l+1为区间长度,取区间[l,l+2k-1],[r-2k+1,r]为两个小区间,显然满足且能找到,时间复杂度为O(1)
模板题代码:
#include<bits/stdc++.h> #define rd(x) x=read() #define N 100005 using namespace std; int n,m; int a[N][25]; inline int read() { int f=1,x=0;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();} while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x*f; } int query(int l,int r) { int k=log2(r-l+1); return max(a[l][k],a[r-(1<<k)+1][k]); } int main() { rd(n); for(int i=1;i<=n;i++)rd(a[i][0]); for(int j=1;j<=21;j++) for(int i=1;i+(1<<j)-1<=n;i++) a[i][j]=max(a[i][j-1],a[i+(1<<(j-1))][j-1]); rd(m); while(m--) { int l,r; rd(l),rd(r); printf("%d\n",query(l,r)); } return 0; }
是不是感觉RMQ很方便呢,RMQ还有许许多多的拓展,下期再见!
标签:++ 技术 阅读 nlog == 长度 ima print 最大的
原文地址:https://www.cnblogs.com/Robin20050901/p/10311438.html