http://poj.org/problem?id=2019 你懂的
题意:给出一个矩阵,输出询问的子矩阵中最大值与最小值的差。
当然是直接RMQ啦!但是对于萌新来说,二维RMQ还是有点懵懵的,QAQ。。。
既然二维的RMQ有点懵懵的,那就先从一维RMQ开始吧!
一维RMQ
基于倍增的思想,我们就可以很暴力地搞出一维RMQ的模板!
(照着kuangbindalao的打的,还没有验证。。但应该不会错吧。。。)
//一维
const int N = 50010;
int dp[N][20];
int mm[N];//是一个辅助数组,帮助查询区间长度是2的多少次方!
//初始化,数组下标从1开始,求最大值
void initRMQ(int n,int b[]){
mm[0] = -1;
for(int i = 1; i <= n; ++ i){
mm[i] = ((i & (i-1) == 0)? mm[i-1]+1:mm[i-1]);
dp[i][0] = b[i];
}
for(int j = 1; j <= mm[n]; ++ j)
for(int i = 1; i +(1<<j)-1 <= n; ++ i)
dp[i][j] = max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
//查询最大值
int rmq(int x,int y){
int k = mm[y-x+1];
return max(dp[x][k], dp[y-(1<<k)+1][k]);
}
其中至关重要的两句话,就是初始化和查询的时候,带max函数的那两句话啦。
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
(
1
<
<
(
j
−
1
)
)
]
[
j
−
1
]
)
dp[i][j] = max(dp[i][j-1],dp[i+(1<<(j-1))][j-1])
dp[i][j]=max(dp[i][j−1],dp[i+(1<<(j−1))][j−1])
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示以
i
i
i为起点,长度为
2
j
−
1
2^{j}-1
2j−1的区间内的最大值。
所以状态转移就是
当
前
要
求
的
区
间
=
之
前
求
过
的
两
段
刚
好
可
以
拼
成
当
前
区
间
的
区
间
取
最
大
值
当前要求的区间=之前求过的两段刚好可以拼成当前区间的区间取最大值
当前要求的区间=之前求过的两段刚好可以拼成当前区间的区间取最大值
啊。。图好丑。。反正就是这么个意思。。。
嗯。。那查询的时候,大概就是这么个意思:
黑色的是查询区间。
呼,一维的大概就是这样啦!
二维RMQ
初始化的时候,我们是这样初始化的(我真的不是故意这么缩进的!但是不这样的话阅读体验着实有些差。。)
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
dp[i][j][0][0] = val[i][j];
for(int ii = 0; ii <= mm[n]; ++ ii)
for(int jj = 0;jj <= mm[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)
dp[i][j][ii][jj] = max(dp[i][j][ii-1][jj],
dp[i+(1<<(ii-1))][j][ii-1][jj]);
else
dp[i][j][ii][jj] = max(dp[i][j][ii][jj-1],
dp[i][j+(1<<(jj-1))][ii][jj-1]);
其实和一维差不多啦!把线看成面就好了~(或者把线画得很粗很粗的)
但是这样的话还是有个小问题(所以需要特判 i i 和 j j 嘛~)
比如我们是横向拓展的,那纵向(也就是很粗很粗的线的直径)该怎么拓展呢。
当然是特判一下,和横向一样的拓展了 = = ~
言归正传回归本题(没什么可说的了)。。。。。。
AC代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
/*********************
//一维
const int N = 50010;
int dp[N][20];
int mm[N];
//初始化,数组下标从1开始,求最大值
void initRMQ(int n,int b[]){
mm[0] = -1;
for(int i = 1; i <= n; ++ i){
mm[i] = ((i & (i-1) == 0)? mm[i-1]+1:mm[i-1]);
dp[i][0] = b[i];
}
for(int j = 1; j <= mm[n]; ++ j)
for(int i = 1; i +(1<<j)-1 <= n; ++ i)
dp[i][j] = max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
//查询最大值
int rmq(int x,int y){
int k = mm[y-x+1];
return max(dp[x][k], dp[y-(1<<k)+1][k]);
}
*********************/
/******************************************/
//二维
//数组下标1开始,预处理复杂度n*m*logn*logm
int val[310][310];
int dp[310][310][9][9];//最大值
int mm[310];//二进制数-1,使用前初始化
void initRMQ(int n,int m){
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
dp[i][j][0][0] = val[i][j];
for(int ii = 0; ii <= mm[n]; ++ ii)
for(int jj = 0;jj <= mm[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) dp[i][j][ii][jj] = max(dp[i][j][ii-1][jj], dp[i+(1<<(ii-1))][j][ii-1][jj]);
else dp[i][j][ii][jj] = max(dp[i][j][ii][jj-1], dp[i][j+(1<<(jj-1))][ii][jj-1]);
}
//查询矩形内最大值(x1<=x2,y1<=y2)
int rmq(int x1,int y1,int x2,int y2){
int k1 = mm[x2-x1+1];
int k2 = mm[y2-y1+1];
x2 -= (1<<k1)-1;
y2 -= (1<<k2)-1;
return max(max(dp[x1][y1][k1][k2],dp[x1][y2][k1][k2]),max(dp[x2][y1][k1][k2], dp[x2][y2][k1][k2]));
}
int main(){
//在外边对mm初始化
mm[0] = -1;
for(int i = 1; i <= 305; ++ i)
mm[i] = (i&(i-1)) == 0 ? mm[i-1]+1:mm[i-1];
int n,m,Q;
int r1,r2,c1,c2;
while(scanf("%d%d",&n,&m) == 2){
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
scanf("%d",&val[i][j]);
initRMQ(n,m);
scanf("%d",&Q);
while(Q --){
scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
if(r1 > r2) swap(r1,r2);
if(c1 > c2) swap(c1,c2);
int tmp = rmq(r1,c1,r2,c2);
printf("%d\n",tmp);
}
}
return 0;
}