對於數組A[i] ,現要求下標l 到 r 的最值。
這是很常見的題目。
其解決方法也有很多,這兩天重新學習了一下,終於更明白了。
方法之一:
樸素算法(搜索):也就是一個一個看。。。。
對於某一次求複雜度最大爲O(N) q 次的話那就是O(qN)了
好大。。
//對於minn同理
int maxn=-inf;
for(int i=l ;i<=r ;i++)
{
if(maxn<a[i])maxn = a[i];
}
printf("%d",maxn);
方法二:線段樹
時間複雜度O(qlogn) 額還好
用線段樹來解決區間最值問題是很經典的 就不多贅述了
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 10000;
int MAX[maxn<<2];
void PushUP(int rt) {
MAX[rt] = max(MAX[rt<<1] , MAX[rt<<1|1]);
}
void build(int l,int r,int rt) {
if (l == r) {
scanf("%d",&MAX[rt]);
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return MAX[rt];
}
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret = max(ret , query(L , R , lson));
if (R > m) ret = max(ret , query(L , R , rson));
return ret;
}
int main(){
int n,m;
scanf("%d%d",&n,&m); // n 是長度 m是查詢次數
build(1, n, 1);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",query(a , b , 1 , n , 1));
}
return 0;
}
方法三:
RMQ 之ST 算法
O(nlogn)時間內進行預處理,然後在O(1)時間內回答每個查詢。
實質上這是一個動態規劃
F[1,0]表示第1個數起,長度爲2^0=1的最大值
f[i][j] 從 i 長度爲2^j 即 1<
void rmqst(int n){
for(int j=1;j<=20 ;j++) //注意順序!!!!!
for(int i=1 ;i<=n ;i++)
{if((i+(1<<j)-1) <=n)
{
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]); //最小
}
}
}
先更新所有長度爲F[i,0]即1個元素,然後通過2個1個元素的最值,
獲得所有長度爲F[i,1]即2個元素的最值,然後再通過2個2個元素的最值,獲得所有長度爲
F[i,2]即4個元素的最值,以此類推更新所有長度的最值。
然後查詢
對於區間爲【i,j】
最值maxn = max(maxsum[l,k], maxsum[r-2^k+1,k])
k是滿足2^k<=r-l+1(即長度)的最大的k,即(int)k=(b-a+1)/log(2.0)。
int maxx,minn;
int k = (int)((log(r-l+1))/log(2.0));
maxx = max(maxsum[l][k] , maxsum[r - (1<<k) +1][k]);
minn = min(maxsum[l][k] , maxsum[r - (1<<k) +1][k]);
我覺得其實上面的方法就夠了
下面這個感覺都好麻煩,沒用過。。。。
方法四:
先規約成LCA(Lowest Common Ancestor),再規約成約束RMQ。
首先根據原數列,建立笛卡爾樹,從而將問題在線性時間內規約爲LCA問題。LCA問題可以在線性時間內規約爲約束RMQ,也就是數列中任意兩個相鄰的數的差都是+1或-1的RMQ問題。