前几天做了学弟出的一道dp,我用了简单的dp结果超时,后来学弟和我说这是一种特殊的dp方法叫rmq,所以在此膜拜一下学弟,受教了
先上百度给的解释
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题。
所以总的来说,主要就是预处理和输出这两大部分
初始化 部分
比如有一组数据 3 2 4 5 6 8 1 2 9 7
我们先将 3 2 4 5 6 8 1 2 9 7存到二维数组每行的开头
然后我们将A两个相隔1的框中值选出最大的放入B中
让后我们将B两个相距2的框中值选出最大的放入C中 (大家可以发现c1里存的其实是a1-a4 中最大值,c2其实a2-a5中的最大值......)
最后让我们将C两个相距4的框中选出最大的放入D中(大家可以发现d1里存的其实是a1-a8 中最大值,c2其实a2-a9中的最大值......)
这时由于8*2=16>10所以结束
这是对找出最大值的初始化,最小值的话只要每次找最小即可
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]); } }
输出 部分
输出其实就是找其公共部分
比如 5-7 7-5+1=3<4
所以只要在b列找第5(a5-a6)个和第6(a6-a7)个取最大即可
比如 5-9 9-5+1=5<8
所以只要在c列找第5(a5-a8)个和第6(a6-a9)个取最大即可
比如 1-9 9-1+1=9>8
所以只要在d列找第1(a1-a8)个和第2(a2-a9)个取最大即可
下面就是学弟出的题在zwu.hustoj.com上的题目(1562)
可以认为序列中的数字都不超过int范围
[ 提交][ 状态]// // main.cpp // rmq // // Created by 5201-mac on 17/7/10. // Copyright © 2017年 5201-mac. All rights reserved. // #include <iostream> #include <cstdio> #include <algorithm> int a[10000]; int rmq[10000][100]; using namespace std; int main() { int n,m; while (scanf("%d%d",&n,&m)!=EOF) { for (int i=0; i<n; i++) { scanf("%d",&a[i]); } for (int i=0; i<n; i++) { rmq[i][0]=a[i]; } for (int k=1; (1<<k)<=n; k++) { for (int l=0;l+(1<<k)-1<n; l++) { rmq[l][k]=max(rmq[l][k-1], rmq[l+(1<<(k-1))][k-1]); } } while (m--) { int l,r; scanf("%d%d",&l,&r); int len=r-l+1; int k=0; int zhan=k; for (; (1<<k)<=len; k++) { zhan=k; } printf("%d\n",max(rmq[l][zhan], rmq[r-(1<<zhan)+1][zhan])); } } // insert code here... //std::cout << "Hello, World!\n"; return 0; }