rmq
RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为 n n n的数列 A A A,
设 A [ i ] A[i] A[i]是要求区间最值的数列, F [ i , j ] F[i, j] F[i,j]表示从第 i i i个数起连续 2 j 2^j 2j个数中的最大值。(DP的状态)
例如:
A数列为:3 2 4 5 6 8 1 2 9 7
F [ 1 , 0 ] F[1,0] F[1,0]表示第1个数起,长度为 2 0 = 1 2^0=1 20=1的最大值,其实就是 3 3 3这个数。同理
F [ 1 , 1 ] = m a x ( 3 , 2 ) = 3 F[1,1] = max(3,2) = 3 F[1,1]=max(3,2)=3
F [ 1 , 2 ] = m a x ( 3 , 2 , 4 , 5 ) = 5 F[1,2]=max(3,2,4,5) = 5 F[1,2]=max(3,2,4,5)=5
F [ 1 , 3 ] = m a x ( 3 , 2 , 4 , 5 , 6 , 8 , 1 , 2 ) = 8 ; F[1,3] = max(3,2,4,5,6,8,1,2) = 8; F[1,3]=max(3,2,4,5,6,8,1,2)=8;
并且我们可以容易的看出 F [ i , 0 ] F[i,0] F[i,0]就等于 A [ i ] A[i] A[i]。(DP的初始值)
这样,DP的状态、初值都已经有了,剩下的就是状态转移方程。
我们把 F [ i , j ] F[i,j] F[i,j]平均分成两段(因为 f [ i , j ] f[i,j] f[i,j]一定是偶数个数字),
从 i i i 到 i + 2 ( j − 1 ) − 1 i + 2 ^ {(j - 1)} - 1 i+2(j−1)−1为一段, i + 2 ( j − 1 ) i + 2 ^ {(j - 1)} i+2(j−1)到 i + 2 j − 1 i + 2 ^ j - 1 i+2j−1为一段
(长度都为 2 ( j − 1 ) 2 ^ {(j - 1)} 2(j−1))。用上例说明,当 i = 1 , j = 3 i=1,j=3 i=1,j=3时就是 3 , 2 , 4 , 5 3,2,4,5 3,2,4,5和 6 , 8 , 1 , 2 6,8,1,2 6,8,1,2这两段。 F [ i , j ] F[i,j] F[i,j]就是这两段各自最大值中的最大值。
于是我们得到了状态转移方程:
F [ i , j ] = m a x ( F [ i , j − 1 ] , F [ i + 2 ( j − 1 ) , j − 1 ] ) F[i, j]=max(F[i,j-1], F[i + 2^{(j-1)},j-1]) F[i,j]=max(F[i,j−1],F[i+2(j−1),j−1])
void RMQ(int num) //预处理
{
for(int i = 1;i <= num; i++){
int tmp; scanf("%d",&tmp);
dp1[i][0] = dp[i][0] = tmp; // 注意理解dp的含义
}
for(int j = 1; (1<<j) <= num; j++)
for(int i = 1; i <= num; i++)
if(i + (1<<j) - 1 <= num)
{
dp1[i][j] = max(dp1[i][j-1], dp1[i+(1<<(j-1))][j-1]);
dp2[i][j] = min(dp2[i][j-1], dp2[i+(1<<(j-1))][j-1]);
}
}
i和j 的位置不能互相调换,先用两个 2 0 2^0 20的来更新一个 2 1 2^1 21 ,扫完一遍后,用两个 2 1 2^1 21 来更新一个 2 2 2^2 22,再扫一遍…
这样并不能直接求区间的最大最小值,查询区间的长度还是二的整数倍
不妨举个例子:
定义一个整数 z z z。查询 [ x , y ] [x,y] [x,y] 区间的最大和最小值,注意区间长度是 y − x + 1 y-x+1 y−x+1
z z z 取到 2 z < = y − y + 1 2^z <= y-y+1 2z<=y−y+1 并且 2 z + 1 > y − x + 1 2^{z+1} > y - x + 1 2z+1>y−x+1
原因:最大值/最小值可以取并操作 :
查询已经预处理好的区间
int z = 0;
while(1<<(z+1) <= (y-x+1)) z++; // 区间取[x, y]
int Max = max(dp1[x][z], dp1[y-(1<<z)+1][z]);
int Min = min(dp2[x][z], dp2[y-(1<<z)+1][z]);