RMQ(Range Minimum/Maximum Query)即区间最值查询:
对于长度为n的数列A,求解若干RMQ(i, j) (i,j<=n),返回A[i]~A[j]的最值。
ST(Sparse Table)可在线处理RMQ问题,可以在O(nlogn)内进行预处理,然后在O(1)内查询。
感谢优秀博客(写的很详细,这里只稍作总结):
https://www.jianshu.com/p/8eebc50ad28a
https://www.cnblogs.com/zyf0163/p/4782133.html
算法思想
- 预处理(O(nlogn))
· 确定数列A[i]至A[j]的最值,两重循环n^2复杂度,dp的思想是:
dp[i][j] 记录第i位~第i位开始的连续2^j位中的最值,dp[i][j]维护的区间是[i,i+2^j-1],构建的过程将区间分成两半,转移方程如下
dp[i][j]=max/min(dp[i][j-1], dp[i+2^(j-1)][j-1])
代码如下:
1 2 3 4 5 6 7 8 9 10 11
| void ST(int n) { for(int j = 1; (1<<j) <= n; ++j) { for(int i = 1; i+(1<<j)-1 <= n; ++i) { maxn[i][j] = max(maxn[i][j-1], maxn[i+(1<<(j-1))][j-1]); minn[i][j] = min(minn[i][j-1], minn[i+(1<<(j-1))][j-1]); } } }
|
当然还有输入时的预处理
1 2 3 4 5 6
| for(int i = 1; i <= n; ++i) { int t; cin >> t; maxn[i][0] = minn[i][0] = t; }
|
- 查询(O(1))
- 查询区间[i, j]的最值
·然而dp能得到[i, i+2^j-1]的最值,这里很容易想到利用k=log2(j-i+1)
,求dp[i][k]就可以吗?
没那么简单,k要取整所以dp范围会缩小,这里返回的是
return max(dp[i][k], dp[j-2^k+1][k])
维护两区间[i][i+2^k-1], [j-2^k+1][j],毫无疑问如果两区间有交集,即i+2^k-1 <= j-2^k+1
这么返回就没问题,关于证明,参考的第二篇博客写的很清楚,这里不再赘述。
————————————————代码如下——————————————————————
1 2 3 4 5 6 7
| void solve(int i, int j) { int k = log2(j-i+1.0); cout << "max:" << max(maxn[i][k], maxn[j-(1<<k)+1][k]) << endl; cout << "min:" << min(minn[i][k], minn[j-(1<<k)+1][k]) << endl; }
|
——————————————————完整代码——————————————————
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| int n, q; typedef long long ll; const int N = 100005; int maxn[N][25]; int minn[N][25]; void ST(int n) { for(int j = 1; j <= 20; ++j) { for(int i = 1; i+(1<<j)-1 <= n; ++i) { maxn[i][j] = max(maxn[i][j-1], maxn[i+(1<<(j-1))][j-1]); minn[i][j] = min(minn[i][j-1], minn[i+(1<<(j-1))][j-1]); } } } void solve(int i, int j) { int k = log2(j-i+1.0); cout << "max:" << max(maxn[i][k], maxn[j-(1<<k)+1][k]) << endl; cout << "min:" << min(minn[i][k], minn[j-(1<<k)+1][k]) << endl; } int main() { cin >> n >> q; for(int i = 1; i <= n; ++i) { int t; cin >> t; maxn[i][0] = minn[i][0] = t; } ST(n); for(;;) { int x, y; cin >> x >> y; solve(x, y); } return 0; }
|