解决RMQ(Range Minimum/Maximum Query)即区间最大最小值问题。
有一个离线算法(ST算法),这个算法是很高效了,时间是O(nlogn):(用O(nlogn)的时间进行预处理,再用O(1)的时间进行区间查询)
1.先是预处理(用动态规划解决)
A数列为:3 2 4 5 6 8 1 2 9 7
F[1,0]表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。同理 F[1,1] = max(3,2) = 3, F[1,2]=max(3,2,4,5) = 5,F[1,3] = max(3,2,4,5,6,8,1,2) = 8;
首先,DP的初始值,即第一次时,每次都是自己本身:F[i,0] = A[i]。
然后是动态转移方程 F[ i ][ j ] = max ( F[ i ] [ j-1 ], F[ i + 2^( j - 1 ) ] [ j - 1 ]);
代码:
1 void RMQ(int num) //预处理->O(nlogn) 2 { 3 //初始化 4 for (int i = 1; i <= num; i++){ 5 minsum[i][0] = a[i]; 6 maxsum[i][0] = a[i]; 7 } 8 9 for (int j = 1; j < 25; j++) 10 for (int i = 1; i <= num; i++) 11 if (i + (1 << j) - 1 <= num) 12 { 13 maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]); 14 minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]); 15 } 16 }
其中 i 和 j 的位置不能变,因为是先更新 1 个元素,再更新两个,四个.....等元素,以此类推更新所有长度的最值。
2.查询
1 //查询 2 int getmin(int x, int y){ 3 int k = (int)(log((double)(y - x + 1)) / log(2.0)); 4 return min(minsum[x][k], minsum[y - (1 << k) + 1][k]); 5 } 6 7 int getmax(int x, int y){ 8 int k = (int)(log((double)(y - x + 1)) / log(2.0)); 9 return max(maxsum[x][k], maxsum[y - (1 << k) + 1][k]); 10 }
对于需要查询的区间[ x, y],区间的差值为 len = y - x + 1;
那么查询的时候 设此时的 j 是 k,那么在查询时,需要 x -- x + 2^k -1 和 y - 2^k + 1 -- y之间,此时就要找到合适的K值。
1 int k=0; 2 while((1<<(k+1))<=y-x+1) 3 k++;
用这个方法找也可以,不过有个更快捷的 k=log2( y - x + 1) 直接进行取值。