RMQ是一种离线算法,通常用于求出区间【l,r】中的最大值或者最小值。
显然这种问题用线段树是能解决的,我之前也一直用的线段树完成这题。
RMQ,简称ST算法。 通过进行(nlogn)的预处理等操作,使询问的时间复杂度 达到 O(1),在此当然也蕴含着dp的思想。
具体:
设立一个数组dp【i】【j】 存着区间【i , i + 2^j-1】的最大值或者最小值,显然【i , i + 2^j-1】可以写成【i , i + 1<<j -1】
同时区间【i , i + 2^j-1】 可以看成 【i,i+2^(j-1)-1】和 【i +2^(j-1), i + 2^j-1】的组成。 即转移方程:【i , i + 2^j-1】=max(【i,i+2^(j-1)-1】 ,【i +2^(j-1), i + 2^j-1】 )
简化后:dp【i】【j】=max(dp【i】【j-1】,dp【i+(1<<(j-1))】【j-1】)
预处理:
void init() { for (int i=1;i<=n;i++){ cin>>dpmax[i][0]; dpmin[i][0]=dpmax[i][0]; } for (int j=1;(1<<j)<=n;j++){ for (int i=1;(i+(1<<j)-1)-1<=n;i++){ dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]); dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]); } } return ; }
询问:
通常给出,左边界 l 和 右边界 r 的确切值,求区间【l,r】的最大值或最小值
此时我们只需要得到满足 2^k<=( r-l+1 ) 的k的最大值
然后把区间分为两个可以相交的区间,分别为【l , l+2^k-1】和 【r-2^k+1 , r 】
简化下就是: dp【l】【k】 和 dp【r-(1<<k)+1】【k】
int query(int l,int r) { int k=0,len=r-l+1; while ((1<<(k+1))<=len){ k++; } return max(dpmax[l][k],dpmax[r-(1<<k)+1][k])-min(dpmin[l][k],dpmin[r-(1<<k)+1][k]); }
1.借鉴博客 求区间最值问题
2.借鉴博客 求lca问题 (对于一棵树,求两个节点的最近公共祖先)