RMQ( 區間最值,區間頻繁次數)

      RMQ (Range Minimum/Maximum Query)問題是指:對於長度爲n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裏的最小(大)值,也就是說,RMQ問題是指求區間最值的問題。

      首先是預處理,用一個DP解決。設a是要求區間最值的數列,f[i,j]表示從第i個數起連續2^j個數中的最大值。例如數列3 2 4 5 6 8 1 2 9 7 ,f[1,0]表示第1個數起,長度爲2^0=1的最大值,其實就是3這個數。f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……從這裏可以看出f[i,0]其實就等於a[i]。這樣,DP的狀態、初值都已經有了,剩下的就是狀態轉移方程。我們把f[i,j](j≥1)平均分成兩段(因爲j≥1時,f[i,j]一定是偶數個數字),從i到i+2^(j-1)-1爲一段,i+2^(j-1)到i+2^j-1爲一段(長度都爲2^(j-1))。用上例說明,當i=1,j=3時就是3,2,4,5 和6,8,1,2這兩段。f就是這兩段的最大值中的最大值。於是我們得到了動規方程F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1])。
接下來是得出最值,也許你想不到計算出f有什麼用處,一般要想計算max還是要O(logn),甚至O(n)。但有一個很好的辦法,做到了O(1)。還是分開來。如在上例中我們要求區間[2,8]的最大值,就要把它分成[2,5]和[5,8]兩個區間,因爲這兩個區間的最大值我們可以直接由f[2,2]和f[5,2]得到。擴展到一般情況,就是把區間[l,r]分成兩個長度爲2^n的區間(保證有f對應)。直接給出表達式:
k:=(int)(ln(r-l+1)/ln(2));
ans:=max(F[l,k],F[r-2^k+1,k]);
這樣就計算了從l開始,長度爲2^k的區間和從r-2^k+1開始長度爲2^k的區間的最大值(表達式比較繁瑣,細節問題如加1減1需要仔細考慮),二者中的較大者就是整個區間[l,r]上的最大值。
區間最大值最小值:

    #include   
    #include   
    #include   
    #include   
    using namespace std;  
    const int MAXN = 100117;  
    int n,query;  
    int num[MAXN];  
      
    int F_Min[MAXN][20],F_Max[MAXN][20];  
      
    void Init()  
    {  
        for(int i = 1; i <= n; i++)  
        {  
            F_Min[i][0] = F_Max[i][0] = num[i];  
        }  
      
        for(int i = 1; (1<

區間出現次數最多的數出現的次數:

#include   
#include   
#include   
using namespace std;  
  
const int maxn = 100017;  
int num[maxn], f[maxn], MAX[maxn][20];  
int n;  
int max(int a,int b)  
{  
    return a>b ? a:b;  
}  
int rmq_max(int l,int r)  
{  
    if(l > r)  
        return 0;  
    int k = log((double)(r-l+1))/log(2.0);  
    return max(MAX[l][k],MAX[r-(1<


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章