RMQ可以用来查找区间最值(最大或最小)
二维RMQ可以用来求子矩阵的最值问题。
RMQ先将所有起点的向后跳2^i的数最值求出来,然后查询的时候再查找两个区间
dp[i][j]表示i~i+2^j-1这2^j个数中的最值,如果用区间表示的话,就是
[i,i+2^j)
这样的话我们可以得到一个公式
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j)][j-1];
i~2^j-1可以分成求两个区间的最值[i,i+2^j)--> max [i,i+2^(j-1)) [i+2^(j-1),i+2^j)
区间的最值是可以合并的。
一维数组使用RMQ的话时间复杂度是
O(n*log(n))-O(1)
代码:
#include <iostream> #include <cstdio> #include <cmath> using namespace std; #define MAXN 200010 const int N_N=log2(MAXN)+2; int n,k; int d[MAXN][N_N]; int a[MAXN]; inline int max(int a, int b) { return a > b ? a : b; } void st() { int m, i, j; for(int i=1;i<=n;i++) d[i][0]=a[i]; //m = (int) (log((double) n) / log(2.0)); m=log2(n); for (j = 1; j <= m; ++j) { for (i = 1; i + (1 << j) - 1 <= n; ++i) d[i][j] = max(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]); } } int rmq(int a, int b) { int m = (int) (log(b - a + 1.0) / log(2.0)); return max(d[a][m], d[b - (1 << m) + 1][m]); } int main() { while(~scanf("%d",&n)){ for(int i=1;i<=n;i++)scanf("%d",&a[i]); int t; st(); scanf("%d",&t); while(t--){ int a,b; scanf("%d%d",&a,&b); printf("%d\n",rmq(a,b)); } } return 0; }
二维RMQ
两种做法:
1) O(n*m*log(m))-O(n)
2) O(n*m*log(n)*log(m)-O(1)
第一种很好理解。
把每一行都当成一维RMQ处理
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <set> #include <vector> #include <map> #include <queue> #include <set> #include <algorithm> using namespace std; #define MAXN 250 int dp[MAXN][MAXN][20]; int dp1[MAXN][MAXN][20]; int a[MAXN][MAXN]; int n,m; void st(){ for(int i=1;i<=n;i++) for(int k=0;(1<<k)<=m;k++){ for(int j=1;j+(1<<k)-1<=m;j++){ if(k==0){ dp[i][j][k]=dp1[i][j][k]=a[i][j]; } else { dp[i][j][k]=max(dp[i][j][k-1],dp[i][j+(1<<(k-1))][k-1]); dp1[i][j][k]=min(dp1[i][j][k-1],dp1[i][j+(1<<(k-1))][k-1]); } } } } int rmq2dmax(int x,int y,int x1,int y1){ int k=log2(y1-y+1); int mm=max(dp[x][y][k],dp[x][y1-(1<<k)+1][k]); for(int i=x+1;i<=x1;i++) mm=max(mm,max(dp[i][y][k],dp[i][y1-(1<<k)+1][k])); return mm; } int rmq2dmin(int x,int y,int x1,int y1){ int k=log2(y1-y+1); int mm=min(dp1[x][y][k],dp1[x][y1-(1<<k)+1][k]); for(int i=x+1;i<=x1;i++) mm=min(mm,min(dp1[i][y][k],dp1[i][y1-(1<<k)+1][k])); return mm; }第二种方法。
可以把一个大矩阵划分成四个小矩阵求最值
但是要考虑到这个矩阵行为1或列为1的情况。需要特判断。
dp[i][j][k][l]-->代表i~i+2^k j~j+2^l 这个矩阵中的最大值
三种情况
k=0 l=0 dp[i][j][k][l]=a[i][j];
k=0 l!=0 dp[i][j][k][l]=max(dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]);
k!=0 l=0 dp[i][j][k][l]=max(dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]);
其他 dp[i][j][k][l]=maxm(dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1],
dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]);
可以拿笔在纸上画画。很容易推出这个方程。
这里有一道题戳我= =用这种方法居然MLE 我去。使用了O(n*m*log(m)-O(n)的复杂度才过。
好了,代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <set> #include <vector> #include <map> #include <queue> #include <set> #include <algorithm> using namespace std; #define MAXN 300 int rec[MAXN][MAXN]; int dp[MAXN][MAXN][10][10]; int dp1[MAXN][MAXN][10][10]; //dp-->max //dp1-->min int n,m; inline int maxm(int a,int b,int c,int d){ if(a<b)a=b; if(a<c)a=c; if(a<d)a=d; return a; } inline int minm(int a,int b,int c,int d){ if(b<a)a=b; if(c<a)a=c; if(d<a)a=d; return a; } void st() { for(int k=0;(1<<k)<=n;k++) for(int l=0;(1<<l)<=m;l++) for(int i=1;i+(1<<k)-1<=n;i++) for(int j=1;j+(1<<l)-1<=m;j++) { if(!k&&!l){ dp1[i][j][k][l]=dp[i][j][k][l]=rec[i][j]; } else if(k==0){ dp[i][j][k][l]=max(dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]); dp1[i][j][k][l]=min(dp1[i][j][k][l-1],dp1[i][j+(1<<(l-1))][k][l-1]); //printf("%d %d\n",dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]); } else if(l==0){ dp[i][j][k][l]=max(dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]); dp1[i][j][k][l]=min(dp1[i][j][k-1][l],dp1[i+(1<<(k-1))][j][k-1][l]); //printf("%d %d\n",dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]); } else { dp[i][j][k][l]=maxm(dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1], dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]); dp1[i][j][k][l]=minm(dp1[i][j][k-1][l-1],dp1[i+(1<<(k-1))][j][k-1][l-1], dp1[i][j+(1<<(l-1))][k-1][l-1],dp1[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]); //printf("%d %d %d %d\n",dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1], //dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]); } //printf("i:%d j:%d k:%d l:%d dp:%d\n",i,j,k,l,dp[i][j][k][l]); //system("pause"); } } int rmq2dmax(int x,int y,int x1,int y1){ //if(x==x1&&y==y1)return dp[x][y][0][0]; int k=log(x1-x+1)/log(2); int l=log(y1-y+1)/log(2); return maxm(dp[x][y][k][l],dp[x1-(1<<k)+1][y][k][l], dp[x][y1-(1<<l)+1][k][l],dp[x1-(1<<k)+1][y1-(1<<l)+1][k][l]); } int rmq2dmin(int x,int y,int x1,int y1){ int k=log(x1-x+1)/log(2); int l=log(y1-y+1)/log(2); return minm(dp1[x][y][k][l],dp1[x1-(1<<k)+1][y][k][l], dp1[x][y1-(1<<l)+1][k][l],dp1[x1-(1<<k)+1][y1-(1<<l)+1][k][l]); }