RMQ問題-----資料結構
阿新 • • 發佈:2018-12-16
大佬錄製的學習視訊:RMQ講解
ST演算法流程
1.預處理
/*RMQ問題的預處理*/ void RMQ(int num)///預處理->O(nlogn) { for(int j=1;j<20;j++)///2^j代表區間的最大長度位置的座標 { for(int i=1;i<=num;i++)///所訪問陣列的長度 if(i+(1<<j)-<=m) { 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]); } } } /*注意i,j代表的內層外層*/
邊界條件f[i][0]=a[i]
2.查詢:
求區間【x,y】最大值得表示式:
k=log (y-x+1)以2為底;///此函式效率不高,可用while(1<<(k+1)<y-x+1) k++;求k
ans=max(f[x][k],f[y-(2^k) +1][k]
例題:
士兵殺敵(三) 時間限制:2000 ms | 記憶體限制:65535 KB 難度:5 描述 南將軍統率著N個士兵,士兵分別編號為1~N,南將軍經常愛拿某一段編號內殺敵數最高的人與殺敵數最低的人進行比較,計算出兩個人的殺敵數差值,用這種方法一方面能鼓舞殺敵數高的人,另一方面也算是批評殺敵數低的人,起到了很好的效果。
所以,南將軍經常問軍師小工第i號士兵到第j號士兵中,殺敵數最高的人與殺敵數最低的人之間軍功差值是多少。
現在,請你寫一個程式,幫小工回答南將軍每次的詢問吧。
注意,南將軍可能詢問很多次。
輸入 只有一組測試資料 第一行是兩個整數N,Q,其中N表示士兵的總數。Q表示南將軍詢問的次數。(1<N<=100000,1<Q<=1000000) 隨後的一行有N個整數Vi(0<=Vi<100000000),分別表示每個人的殺敵數。 再之後的Q行,每行有兩個正正數m,n,表示南將軍詢問的是第m號士兵到第n號士兵。 輸出 對於每次詢問,輸出第m號士兵到第n號士兵之間所有士兵殺敵數的最大值與最小值的差。 樣例輸入 5 2 1 2 6 9 3 1 2 2 4 樣例輸出 1 7
程式碼:
/*此程式碼未經檢驗,樣例已過,第一道RMQ問題,理解著敲的*/
#include<bits/stdc++.h>
using namespace std;
int maxsum[100080][21],minsum[100080][21],n;
void RMQ(int num)///預處理->O(nlogn)
{
for(int j=1;j<20;j++)///2^j代表區間的最大長度位置的座標
{
for(int i=1;i<=num;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]);
}
}
}
/*注意i,j代表的內層外層*/
int solve(int l,int r)///詢問的時候分別求出最小值和最大值的差
{
int k=0;
while((1<<(k+1))<r-l+1) k++;///求出k
int k1=max(maxsum[l][k],maxsum[r+1-(1<<k)][k]);
int k2=min(minsum[l][k],minsum[r+1-(1<<k)][k]);
return k1-k2;
}
int main()
{
int q,x,y,Max,Min;
while(scanf("%d%d",&n,&q)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&minsum[i][0]);
maxsum[i][0]=minsum[i][0];
}
RMQ(n);
while(q--)
{
int c1,c2;
scanf("%d%d",&c1,&c2);
printf("%d\n",solve(c1,c2));
}
}
return 0;
}