日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

线性结构 —— ST 表与 RMQ

發(fā)布時(shí)間:2025/3/17 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线性结构 —— ST 表与 RMQ 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【概述】

RMQ(Range Minimum/Maximum Query),是對(duì)于長(zhǎng)度為 n 的數(shù)列 A,回答若干次詢問(wèn) RMQ(i,j),返回?cái)?shù)列 A 中下標(biāo)在區(qū)間 [i,j] 中的最值,即:區(qū)間最值查詢問(wèn)題

目前常用于解決 RMQ 問(wèn)題的方法為 ST 算法(Sparse Table),利用 ST 算法預(yù)處理打出的表,稱為 ST 表

【ST 算法】

對(duì)于 RMQ 問(wèn)題,給出 n 個(gè)數(shù) m 次詢問(wèn),每次詢問(wèn)區(qū)間最值,當(dāng) m 較小時(shí),使用暴力即可解決,但隨著 m 的增大,O(logn) 的的詢問(wèn)處理已經(jīng)不夠,需要 O(1) 的詢問(wèn)。

而 ST 算法可以在 O(nlogn) 時(shí)間內(nèi)進(jìn)行預(yù)處理,然后在 O(1) 時(shí)間內(nèi)回答每個(gè)查詢,其實(shí)際上就是一種動(dòng)態(tài)規(guī)劃與打表的思想。

1.原理

1)預(yù)處理

要在 O(1) 求出區(qū)間的最值,一個(gè)很自然的想法是用動(dòng)態(tài)規(guī)劃處理的方法,用 dp[i][j] 來(lái)記錄區(qū)間 [i,j] 的最大值,這樣顯然有狀態(tài)轉(zhuǎn)移方程:dp[i][j]=max(dp[i][j-1],a[j]),但這樣預(yù)處理是 O(n*n) 的,還能進(jìn)一步的優(yōu)化。

max 函數(shù)滿足一個(gè)性質(zhì):允許區(qū)間重疊,即 max(i,j,k)=max( max(i,j) , max(j,k) ),也就是說(shuō),可以由兩個(gè)較小的有重疊的區(qū)間,直接推出一個(gè)大區(qū)間,從而減少維護(hù)的區(qū)間數(shù)量。

采用倍增的思想,設(shè) A[i] 是要求區(qū)間最值的數(shù)列,F[i,j] 表示從第 i 個(gè)數(shù)起連續(xù) 2^j 個(gè)數(shù)中的最大值。(DP的狀態(tài))

可以看出 F[i,0] = A[i](DP的初始值)

把 F[i,j] 平均分成兩段 ( F[i,j] 一定是偶數(shù)個(gè)數(shù)字),從 i 到 i+2^(j-1)-1 為一段,i+2^(j-1) 到 i+2^j-1 為一段,長(zhǎng)度均為 2^(j-1)

于是得到:F[i,j] = max( F[i , j-1] , F[i+2^(j-1) , j-1] )(狀態(tài)轉(zhuǎn)移方程)

例如:

A 為:3 2 4 5 6 8

F[1,0] 表示第 1 個(gè)數(shù)起,長(zhǎng)度為 2^0=1 的最大值,其實(shí)就是 3

F[1,1] = max(3,2) = 3

F[1,2] = max(3,2,4,5) = 5

F[1,3] = max(3,2,4,5,6,8,1,2) = 8

2)查詢

假設(shè)要查詢的區(qū)間為 (i,j),那么需要找到覆蓋這個(gè)閉區(qū)間 [i,j] 的最小冪,由于區(qū)間長(zhǎng)度為 j-i+1,因此可以取 k=log2(j-i+1),則有:RMQ(i, j) = max{ F[i,k] , F[ j-2^k+1,k] }

例:要求區(qū)間 [1,5] 的最大值

有:k = log2(5-1+1) = 2

則:RMQ(1,5) = max( F[1,2] , F[5-2^2+1, 2]) = max(F[1,2] , F[2,2])

2.實(shí)現(xiàn)

int dpMax[N][20]; int dpMin[N][20]; int a[N]; void initMax(int n){//初始化最大值查詢for(int i=1;i<=n;i++)dpMax[i][0]=a[i];for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)dpMax[i][j]=max(dpMax[i][j-1],dpMax[i+(1<<(j-1))][j-1]); } int getMax(int L,int R){//查詢最大值//int k = (int)(log10(R-L+1)/log10(2));int k=0;while((1<<(k+1))<=R-L+1)k++;return max(dpMax[L][k],dpMax[R-(1<<k)+1][k]); }void initMin(int n){//初始化最小值查詢for(int i=1;i<=n;i++)dpMin[i][0]=a[i];for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)dpMin[i][j]=min(dpMin[i][j-1],dpMin[i+(1<<(j-1))][j-1]); } int getMin(int L,int R){//查詢最小值//int k = (int)(log10(R-L+1)/log10(2));int k=0;while((1<<(k+1))<=R-L+1)k++;return min(dpMin[L][k],dpMin[R-(1<<k)+1][k]); } int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&a[i]);initMax(n);initMin(n);for(int i=1;i<=m;i++){int left,right;scanf("%d%d",&left,&right);int maxx=getMax(left,right);int minn=getMin(left,right);printf("Max=%d\n",maxx);printf("Min=%d\n",minn);}return 0; }

【二維 RMQ】

一維 RMQ 問(wèn)題是求一個(gè)數(shù)列 A 中的最值,而二維 RMQ 問(wèn)題是求一個(gè) n*m 的矩陣中,某個(gè)子矩陣內(nèi)的最值

1.原理

1)初始化

設(shè) F[i][j][ii][jj] = x 表示以 (i, j) 為左上角,以 (i+(1<<ii)-1, j+(1<<jj)-1) 為右下角的矩陣內(nèi)的最大值(DP的狀態(tài))

易知:F[i][j][0][0] = G[i][j](DP的初始值)

假設(shè) F[i][j][ii][jj] 中的 ii 不為 0,那么 F[i][j][ii][jj] = max(F[i][j][ii-1][jj], F[i+(1<<ii)][j][ii-1][jj] )?(狀態(tài)轉(zhuǎn)移方程)

如果 ii 為 0,那么就按 jj 來(lái)求,即 F[i][j][ii][jj] = max(F[i][j][ii][jj-1] , F[i][j+(1<<(jj-1))][ii][jj-1])?(狀態(tài)轉(zhuǎn)移方程)

簡(jiǎn)單來(lái)說(shuō),就是將二維問(wèn)題轉(zhuǎn)變?yōu)橐痪S問(wèn)題求解

2)查詢

對(duì)于一個(gè)以 (x1,y1) 為左上角,以 (x2,y2) 為右下角的矩形,將其分成四小塊,這四小塊可能有重合部分,但他們共同構(gòu)成了目標(biāo)矩形:

  • F[x1][y1][ii][jj]
  • F[x1][y2-(1<<jj)+1][ii][jj]
  • F[x2-(1<<ii)+1][y1][ii][jj]
  • F[x2-(1<<ii)+1][y2-(1<<jj)+1][ii][jj]

那么,可先求解第一、二小塊的最值,第三、四小塊的最值,最后再求整體最值

即:

  • temp1 = max(F[x1][y1][ii][jj] , F[x1][y2-(1<<jj)+1][ii][jj])
  • temp2 = max(F[x2-(1<<ii)+1][y1][ii][jj] ,F[x2-(1<<ii)+1][y2-(1<<jj)+1][ii][jj] )

最終結(jié)果為:res = max(temp1,temp2)

2.實(shí)現(xiàn)

int G[N][N]; int dpMin[N][N][10][10]; int dpMax[N][N][10][10]; void initRMQ(int n,int m){//對(duì)n*m的矩陣初始化RMQ且矩陣下標(biāo)從1開(kāi)始for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)dpMin[i][j][0][0]=dpMax[i][j][0][0]=G[i][j];for(int ii=0;(1<<ii)<=n;ii++){for(int jj=0;(1<<jj)<=m;jj++){if(ii+jj){for(int i=1;i+(1<<ii)-1<=n;i++){for(int j=1;j+(1<<jj)-1<=m;j++){if(ii){dpMin[i][j][ii][jj] = min(dpMin[i][j][ii-1][jj] , dpMin[i+(1<<(ii-1))][j][ii-1][jj]);dpMax[i][j][ii][jj] = max(dpMax[i][j][ii-1][jj] , dpMax[i+(1<<(ii-1))][j][ii-1][jj]);}else{dpMin[i][j][ii][jj] = min(dpMin[i][j][ii][jj-1] , dpMin[i][j+(1<<(jj-1))][ii][jj-1]);dpMax[i][j][ii][jj] = max(dpMax[i][j][ii][jj-1] , dpMax[i][j+(1<<(jj-1))][ii][jj-1]);}}}}}} } int getMax(int x1,int y1,int x2,int y2){//RMQ查詢最大值int k1=0,k2=0;while((1<<(k1+1))<=x2-x1+1)k1++;while((1<<(k2+1))<=y2-y1+1)k2++;x2=x2-(1<<k1)+1;y2=y2-(1<<k2)+1;int temp1=max(dpMax[x1][y1][k1][k2],dpMax[x1][y2][k1][k2]);int temp2=max(dpMax[x2][y1][k1][k2],dpMax[x2][y2][k1][k2]);return max(temp1,temp2); } int getMin(int x1,int y1,int x2,int y2){//RMQ查詢最小值int k1=0,k2=0;while((1<<(k1+1))<=x2-x1+1)k1++;while((1<<(k2+1))<=y2-y1+1)k2++;x2=x2-(1<<k1)+1;y2=y2-(1<<k2)+1;int temp1=min(dpMin[x1][y1][k1][k2],dpMin[x1][y2][k1][k2]);int temp2=min(dpMin[x2][y1][k1][k2],dpMin[x2][y2][k1][k2]);return min(temp1,temp2); } int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)scanf("%d",&G[i][j]);initRMQ(n,m);int q;scanf("%d",&q);while(q--){int r1,c1,r2,c2;scanf("%d%d%d%d",&r1,&c1,&r2,&c2);int maxx=getMax(r1,c1,r2,c2);int minn=getMin(r1,c1,r2,c2);printf("max=%d\n",maxx);printf("min=%d\n",minn);}return 0; }

【例題】

  • Find the hotel(HDU-3193)(一維 RMQ):點(diǎn)擊這里
  • Check Corners(HDU-2888)(二維 RMQ):點(diǎn)擊這里
  • 平衡的陣容(洛谷-P2880)(RMQ 求極差):點(diǎn)擊這里
    同題:Balanced Lineup(POJ-3264):點(diǎn)擊這里
  • A Magic Lamp(HDU-3183)(RMQ+模擬 ):點(diǎn)擊這里
  • 總結(jié)

    以上是生活随笔為你收集整理的线性结构 —— ST 表与 RMQ的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。