RMQ 區間最值查詢演算法
RMQ(Range Minimum/Maximum Query),對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裡的最小(大)值。
主要方法:
- 樸素(即搜尋),複雜度為O(n)
- 線段樹能在對數時間內在陣列區間上進行更新與查詢。預處理 O(n), 查詢 O(logn)
定義線段樹在區間[i, j] 上如下:
第一個節點維護著區間 [i, j] 的資訊。
if i<j , 那麼左孩子維護著區間[i, (i+j)/2] 的資訊,右孩子維護著區間[(i+j)/2+1, j] 的資訊。
可知 N 個元素的線段樹的高度 為 [logN] + 1(只有根節點的樹高度為0) .
下面是區間 [0, 9] 的一個線段樹:
線段樹和堆有一樣的結構, 因此如果一個節點編號為 x ,那麼左孩子編號為2*x 右孩子編號為2*x+1.
使用線段樹解決RMQ問題,關鍵維護一個數組M[num],num=2^(線段樹高度+1).
M[i]:維護著被分配給該節點(編號:i 線段樹根節點編號:1)的區間的最小值元素的下標。 該陣列初始狀態為-1.#include<iostream> using namespace std; #define MAXN 100 #define MAXIND 256 //線段樹節點個數 //構建線段樹,目的:得到M陣列. void initialize(int node, int b, int e, int M[], int A[]) { if (b == e) M[node] = b; //只有一個元素,只有一個下標 else { //遞迴實現左孩子和右孩子 initialize(2 * node, b, (b + e) / 2, M, A); initialize(2 * node + 1, (b + e) / 2 + 1, e, M, A); //search for the minimum value in the first and //second half of the interval if (A[M[2 * node]] <= A[M[2 * node + 1]]) M[node] = M[2 * node]; else M[node] = M[2 * node + 1]; } } //找出區間 [i, j] 上的最小值的索引 int query(int node, int b, int e, int M[], int A[], int i, int j) { int p1, p2; //查詢區間和要求的區間沒有交集 if (i > e || j < b) return -1; //if the current interval is included in //the query interval return M[node] if (b >= i && e <= j) return M[node]; //compute the minimum position in the //left and right part of the interval p1 = query(2 * node, b, (b + e) / 2, M, A, i, j); p2 = query(2 * node + 1, (b + e) / 2 + 1, e, M, A, i, j); //return the position where the overall //minimum is if (p1 == -1) return M[node] = p2; if (p2 == -1) return M[node] = p1; if (A[p1] <= A[p2]) return M[node] = p1; return M[node] = p2; } int main() { int M[MAXIND]; //下標1起才有意義,儲存下標編號節點對應區間最小值的下標. memset(M,-1,sizeof(M)); int a[]={3,1,5,7,2,9,0,3,4,5}; initialize(1, 0, sizeof(a)/sizeof(a[0])-1, M, a); cout<<query(1, 0, sizeof(a)/sizeof(a[0])-1, M, a, 0, 5)<<endl; return 0; }
- ST演算法(Sparse
Table):它是一種動態規劃的方法。 預處理O(nlogn) 查詢O(1); (不能用來查詢動態區間)
以最小值為例。a為所尋找的陣列.
用一個二維陣列f(i,j)記錄區間[i,i+2^j-1](持續2^j個)區間中的最小值。其中f[i,0] = a[i];
所以,對於任意的一組(i,j),f(i,j) = min{ f( i , j-1 ) , f( i+2^(j-1), j-1) }來使用動態規劃計算出來。
這個演算法的高明之處不是在於這個動態規劃的建立,而是它的查詢:它的查詢效率是O(1).
假設我們要求a中區間[m,n]的最小值,找到一個數k使得2^k<n-m+1.
這樣,可以把這個區間分成兩個部分:[m,m+2^k-1]和[n-2^k+1,n].我們發現,這兩個區間是已經初始化好的.
前面的區間是f(m,k),後面的區間是f(n-2^k+1,k).
這樣,只要看這兩個區間的最小值,就可以知道整個區間的最小值!
以下為ST演算法實現最小值查詢。
#include<iostream>
#include<cmath>
using namespace std;
#define MM 100010
#define MAXN 500
#define MAXM 500
int Rmin[MM][20], Rmax[MM][20];
int RminID[MM][20], RmaxID[MM][20];
inline int min(const int &a, const int &b)
{
if(a>b) return b;
return a;
}
inline int max(const int &a,const int &b)
{
if(a>b) return a;
return b;
}
void makeRmq(int len,int b[]) /*求最值*/
{
int i,j;
for(i=0; i<len; i++)
Rmin[i][0] = Rmax[i][0] = b[i];
for(j=1; (1<<j)<=len; j++)
for(i=0; i+(1<<j)-1<len; i++)
{
Rmin[i][j] = min(Rmin[i][j-1], Rmin[i+(1<<(j-1))][j-1]);
Rmax[i][j] = max(Rmax[i][j-1], Rmax[i+(1<<(j-1))][j-1]);
}
}
inline int rmqMin(int left,int right)
{
int k = (int)( log((right-left+1)*1.0) / log(2.0) );
return min(Rmin[left][k],Rmin[right-(1<<k)+1][k]);
}
inline int rmqMax(int left,int right)
{
int k = (int)( log((right-left+1)*1.0) / log(2.0) );
return max(Rmax[left][k],Rmax[right-(1<<k)+1][k]);
}
void makeRmqIndex(int len,int b[]) /*求最值下標*/
{
int i,j;
for(i=0; i<len; i++)
RminID[i][0] = RmaxID[i][0] = i;
for(j=1; (1<<j)<=len; j++)
for(i=0; i+(1<<j)-1<len; i++)
{
RminID[i][j] = b[ RminID[i][j-1] ] < b[ RminID[i+(1<<(j-1))][j-1] ] ? RminID[i][j-1] : RminID[i+(1<<(j-1))][j-1];
RmaxID[i][j] = b[ RmaxID[i][j-1] ] > b[ RmaxID[i+(1<<(j-1))][j-1] ] ? RmaxID[i][j-1] : RmaxID[i+(1<<(j-1))][j-1];
}
}
inline int rmqMinIndex(int left,int right,int b[])
{
int k = (int)( log((right-left+1)*1.0) / log(2.0) );
return b[ RminID[left][k] ] < b[ RminID[right-(1<<k)+1][k] ] ? RminID[left][k] : RminID[right-(1<<k)+1][k];
}
inline int rmqMaxIndex(int left,int right,int b[])
{
int k = (int)( log((right-left+1)*1.0) / log(2.0) );
return b[ RmaxID[left][k] ] > b[ RmaxID[right-(1<<k)+1][k] ] ? RmaxID[left][k] : RmaxID[right-(1<<k)+1][k];
}
int main()
{
int a[10];
for(int i=0;i<10;++i) a[i] = i;
makeRmq(10, a);
makeRmqIndex(10, a);
cout<<rmqMaxIndex(0,6,a)<<endl;
cout<<rmqMaxIndex(4,8,a)<<endl;
cout<<rmqMax(1,9)<<endl;
cout<<rmqMax(5,8)<<endl;
cout<<rmqMinIndex(0,6,a)<<endl;
cout<<rmqMinIndex(4,8,a)<<endl;
cout<<rmqMin(1,9)<<endl;
cout<<rmqMin(5,8)<<endl;
return 0;
}
二維RMQ,只有min,max ,minID,maxID按照上面1維的改就OK了
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
/*二維rmq
*和一維rmq的思路一樣
*用dp[row][col][i][j]表示(row,col)到(row+2^i,col+2^j)矩形內的最小值
*查詢的時候
* int kx = log(double(x2 - x1 +1)) / log(2.0);
int ky = log(double(y2 - y1 +1)) / log(2.0);
int m1 = dp[x1][y1][kx][ky];
int m2 = dp[x2-(1<<kx)+1][y1][kx][ky];
int m3 = dp[x1][y2-(1<<ky)+1][kx][ky];
int m4 = dp[x2-(1<<kx)+1][y2-(1<<ky)+1][kx][ky];
*取4個值裡面的最小值(有種二分的思想)
*/
const int maxn = 301;
int mat[maxn][maxn];
int Rmin[maxn][maxn][9][9];
inline int max(const int &a,const int &b)
{
if(a > b) return a;
return b;
}
inline int min(const int &a,const int &b)
{
if(a < b) return a;
return b;
}
void makeRmq_2d(const int rowlen,const int collen)
{
for(int row = 1 ; row <= rowlen ; row++)
for(int col = 1 ; col <= collen ; col++)
Rmin[row][col][0][0] = mat[row][col];
int t = log((double)collen) / log(2.0);
for(int i = 0 ; i <= t ; i++)
{
for(int j = 0 ; j <= t ; j++)
{
if(i == 0 && j == 0)
continue;
for(int row = 1 ; row+(1<<i)-1 <= rowlen ; row++)
{
for(int col = 1 ; col+(1<<j)-1 <= collen ; col++)
{
if(i == 0)
{
Rmin[row][col][i][j] = max(Rmin[row][col][i][j-1] , Rmin[row][col+(1<<(j-1))][i][j-1]);
}
else
{
Rmin[row][col][i][j] = max(Rmin[row][col][i-1][j] , Rmin[row+(1<<(i-1))][col][i-1][j]);
}
}
}
}
}
}
int query_2d(int x1,int x2,int y1,int y2)
{
int kx = log(double(x2 - x1 +1)) / log(2.0);
int ky = log(double(y2 - y1 +1)) / log(2.0);
int m1 = Rmin[x1][y1][kx][ky];
int m2 = Rmin[x2-(1<<kx)+1][y1][kx][ky];
int m3 = Rmin[x1][y2-(1<<ky)+1][kx][ky];
int m4 = Rmin[x2-(1<<kx)+1][y2-(1<<ky)+1][kx][ky];
int ans = max( max(m1,m2), max(m3,m4) );
return ans;
}
int main(int argc, char** argv)
{
cout<<"NO DEMO!"<<endl;
return 0;
}
hdu 2888
#include <stdio.h>
#include <string.h>
#define MAX 301
#define max(a,b) ((a)>(b)?(a):(b))
int flag,power[MAX];
int n,m,q,arr[MAX][MAX];
struct RMQ{
int dp[MAX][MAX][9][9];
void Create();
int Query(int rowl,int rowr,int col,int cor);
}rmq;
void RMQ::Create() {
int i,j,k,s,limitn,limitm;
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
dp[i][j][0][0] = arr[i][j];
for (i = 0; i <= power[n]; ++i)
for (j = 0; j <= power[m]; ++j) {
if (i == 0 && j == 0) continue;
limitn = n + 1 - (1<<i);
limitm = m + 1 - (1<<j);
for (k = 1; k <= limitn; ++k)
for (s = 1; s <= limitm; ++s) {
if (i == 0)
dp[k][s][i][j] = max(dp[k][s][i][j-1],dp[k][s+(1<<(j-1))][i][j-1]);
else
dp[k][s][i][j] = max(dp[k][s][i-1][j],dp[k+(1<<(i-1))][s][i-1][j]);
}
}
}
int RMQ::Query(int r1, int r2, int c1, int c2) {
int temp = 0;
int rk = power[r2-r1+1];
int ck = power[c2-c1+1];
temp = max(temp,dp[r1][c1][rk][ck]);
temp = max(temp,dp[r1][c2-(1<<ck)+1][rk][ck]);
temp = max(temp,dp[r2-(1<<rk)+1][c1][rk][ck]);
temp = max(temp,dp[r2-(1<<rk)+1][c2-(1<<ck)+1][rk][ck]);
if (temp == arr[r1][c1] || temp == arr[r2][c1]
|| temp == arr[r1][c2] || temp == arr[r2][c2])
flag = 1;
return temp;
}
void input (int &a) {
char c, f;
while (((c = getchar()) < '0' || f > '9') );
for (a = 0; c >= '0' && c <= '9'; c = getchar())a = a * 10 + c - '0';
}
int main()
{
int i,j,k = 0;
int t,a,b,c,d;
for (i = 1; i <= 300; ++i)
if (i < (1<<k)) power[i] = k - 1;
else power[i] = k,k++;
while (scanf("%d%d",&n,&m) != EOF) {
for (i = 1; i <= n; ++i)
for (j = 1; j <= m; ++j)
input(arr[i][j]);//scanf("%d",&arr[i][j]);
rmq.Create();
//scanf("%d",&q);
input(q);
while (q--) {
input(a),input(b);
input(c),input(d);
//scanf("%d%d%d%d",&a,&b,&c,&d);
flag = 0;
k = rmq.Query(a,c,b,d);
if (flag) printf("%d yes\n",k);
else printf("%d no\n",k);
}
}
}
相關推薦
RMQ 區間最值查詢演算法
RMQ(Range Minimum/Maximum Query),對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j裡的最小(大)值。 主要方法: 樸素(即搜尋),複雜度為O(n)線段樹能在對數時間內在陣列區間上進行
RMQ區間最值查詢SparseTable演算法
//一維區間最值查詢模板稀疏表sparse table演算法 //區間最值查詢--線段樹--RMQ //區間連續和--線段樹 //區間第k大--快排--劃分樹(線段樹?) --主席樹 //二維區間最值
nyoj 119士兵殺敵(三)(線段樹區間最值查詢,RMQ算法)
信息 include out online log 每次 left 一行 [0 題目119 題目信息 執行結果 本題排行 討論區 士兵殺敵(三) 時間限制:2000 ms | 內存限制:65535 KB 難度:5
區間最值查詢(RMQ)
ST演算法 預處理第i位起連續2^k個數的最大值,快速查詢。 說明:定義陣列dp[i][j]表示 從第i位起連續2^j個數 的最大值。 (區間內有2^j個數) 例子: 2 5 9 6 3 1
ST表-求RMQ(區間最值)問題。
Code: #include<bits/stdc++.h> using namespace std; const int MAX = 2000; int n, m, x, y; int a[MAX + 5], lg[MAX + 5], maxn[MAX + 5][MAX + 5];
hdu 1754 I Hate It(線段樹 單點替換區間 最值查詢)
Problem Description 很多學校流行一種比較的習慣。老師們很喜歡詢問,從某某到某某當中,分數最高的是多少。 這讓很多學生很反感。 不管你喜不喜歡,現在需要你做的是,就是按照老師的要求,寫一個程式,模擬老師的詢問。當然,老師有時候需要更新某位同學的成績。
rmq區間最值
const int maxn=1000; const int maxb=10;//maxb=log2(maxn) int n; int a[maxn+5]; int d[maxn+5][maxb+5]
(一)線段樹入門--區間最值查詢
這是一篇入門文章,不過需要你知道啥是二叉樹,並且知道遞迴,本文會持續更新,時間看作者心情。 線段樹 描述 分類:二叉樹搜尋樹 節點結構: struct node { int l,r;//範圍【l,r】 }tr[100];
RMQ區間最值 倍增求法
void init_rmq(int n) { for(int i=1;i<=n;i++) f[i][0]=a[i]; for(int j=1;j<=20;j++)
RMQ(區間最值問題)
ont 技術分享 要求 span max image range style 問題 問題: RMQ (Range Minimum/Maximum Query)問題是指:對於長度為n的數列A,回答若幹詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,
快速查詢區間最值——RMQ演算法(ST實現)
概述 RMQ(Range Minimum/Maximum Query),即區間最值查詢,是指這樣一個問題:對於長度為n的數列A,回答若干詢問RMQ(A,i,j)(i,j<=n),返回數列A中下標在i,j之間的最小/大值。這兩個問題是在實際應用中經常遇到
快速查詢區間最值——RMQ演算法(線段樹實現程式碼)
要求找出區間內的最大最小值的差。 #include<stdio.h> #include<string.h> #include<math.h> #define lso
hdu1754-I Hate It 線段樹RMQ演算法求區間最值問題
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total
RMQ(快速查詢區間最值)
#include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h&g
RMQ區間最大值與最小值查詢
pan arp max class spa div light min csharp RMQ復雜度:建表$O\left ( nlgn \right ) $,查詢$O\left ( 1 \right )$ ll F_Min[maxn][20],F_Max[max
【線段樹查詢區間最值】poj 3264 Balanced Lineup
truct pri scan aps pre sca print logs oid 1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 const i
RMQ 區間最大值最小值 最頻繁次數
2.0 spa log mes num cst color 順序 max 區間的最大值和最小值 #include <cstdio> #include <cstring> #include <cmath> #include <ios
筆記:RMQ(區間最值)之ST算法
運算 不變 想要 parse 計算機語言 c++ 是我 動態規劃 容易 RMQ(區間最值)之ST算法 RMQ即Range Minimum/Maximun Query 中文意思:查詢一個區間的最小值/最大值 比如有這樣一個數組:A{3 2 4 5 6 8 1 2 9 7},
BZOJ1513:樹套樹之線段樹套線段樹實現二維區間修改和最值查詢
name names algo getch oid min 投影 協調 pan 我們經常提及的二維線段樹有兩種寫法,一種是四分樹,一種是樹套樹,寫成四分樹的都是神仙。 樹套樹寫法還是比較好理解的,不過要是讓自己硬套的話可能很不容易套出來的 這裏的二維線段樹,外層線段樹是對方
RMQ算法區間最值
nbsp 覆蓋 main 明顯 else while int 最大值 turn 問題類型:是多次詢問一個大區間裏子區間的最值問題 dp + 位運算的思想處理 rmax[i][j]表示從i開始到i + 2^j - 1的區間裏的最大值dp[i][j] ==== (i,i + 2