现在的位置: 首页 > 综合 > 正文

ZOJ 2859 二维RMQ问题 Matrix Searching

2013年05月15日 ⁄ 综合 ⁄ 共 2901字 ⁄ 字号 评论关闭

题目:

Matrix Searching


Time Limit: 10 Seconds      Memory Limit: 32768 KB


Given an n*n matrix A, whose entries Ai,j are integer numbers ( 1 <= i <= n, 1 <= j <= n ). An operation FIND the minimun number in a given ssub-matrix.

Input

The first line of the input contains a single integer T , the number of test cases.

For each test case, the first line contains one integer n (1 <= n <= 300), which is the sizes of the matrix, respectively. The next n lines with n integers each gives the elements of
the matrix.

The next line contains a single integer N (1 <= N <= 1,000,000), the number of queries. The next N lines give one query on each line, with four integers r1, c1, r2, c2 (1 <= r1 <= r2
<= n, 1 <= c1 <= c2 <= n), which are the indices of the upper-left corner and lower-right corner of the sub-matrix in question.

Output

For each test case, print N lines with one number on each line, the required minimum integer in the sub-matrix.

Sample Input

1
2
2 -1
2 3
2
1 1 2 2
1 1 2 1

Sample Output

-1
2

题解:

题目显然是RMQ问题,而且是二维的。

大意是讲,给定一个n*n(n <= 300)的矩阵,然后是(T <= 10^6)次询问,每次询问某个子矩阵中的最小值。

想必大家对RMQ问题不是特别陌生,说起来,ST算法的核心就在于“二分”,当然不是传统意义上的二分了,毕竟是二维的,要分类来进行二分处理。

简单地说,定义dp[row][col][i][j] 表示[row,row+2^i-1] x [col,col+2^j-1] 二维区间内的最小值。

那么,有:

dp[row][col][i][j]

= min( [row,row+ 2^(i-1)-1] x [col,col+2^j-1] ,
[row+2^(i-1),row+2^i-1]
 x [col,col+2^j-1] )

(左半区的最小值 和 右半区的最小值)

= min( dp[row][col][i-1][j] , dp[row + (1<<(i-1))][col][i-1][j] )

(左半区的最小值 和 右半区的最小值,相当于固定了y轴坐标不变,将区域分割成左右两个区)

同样的,也有:

dp[row][col][i][j]=

= min( [row,row+ 2^i-1] x [col,col+2^(j-1)-1] ,
[row,row+2^i-1]
 x [col+2^(i-1),col+2^j-1]
)

(上半区的最小值 和 下半区的最小值)

= min( dp[row][col][i][j-1] ,
dp[row][col + (1<<(i-1))][i][j-1] )
 

(上半区的最小值 和 下半区的最小值,相当于固定了x轴坐标不变,将区域分割成上下两个区)

时间复杂度是O(N * M * log N * log M)

空间复杂度是O(N * M * log N * log M)

已经非常的高效了。

代码示例:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

const int maxn = 310;

int N;
int val[maxn][maxn];

//2D RMQ
//dp[row][col][i][j] 表示[row,row+2^i-1]x[col,col+2^j-1] 二维区间内的最小值.
//dp[row][col][i][j] = 
//	min( dp[row][col][i-1][j], dp[row + (1<<(i-1))][col][i-1][j] )
//    	min( dp[row][col][i][j-1], dp[row][col + (1<<(j-1))][i][j-1] )

int code(int i){
    return log(double(i)) / log(2.0);
}

int dp[maxn][maxn][9][9];
void initRMQ(int N,int M){
	for (int row=1;row<=N;row++){
	for (int col=1;col<=M;col++){
		dp[row][col][0][0]=val[row][col];
	}
	}
	int n = code(N);
	int m = code(M);
	for (int i=0;i<=n;i++){
	for (int j=0;j<=m;j++){
		if(i == 0 && j==0)continue;
		for (int row=1;row+(1<<i)-1<=N;row++){
		for (int col=1;col+(1<<j)-1<=M;col++){
			if (i==0){//水平划分
				dp[row][col][i][j] = min(dp[row][col][i][j-1] , dp[row][col+(1<<(j-1))][i][j-1]);
			}
			else {//竖直划分
				dp[row][col][i][j] = min(dp[row][col][i-1][j] , dp[row+(1<<(i-1))][col][i-1][j]);
			}
		}
		}
	}
	}
}

int RMQ(int x1,int x2,int y1,int y2){
	int kx = code(x2-x1+1);
	int ky = code(y2-y1+1);
	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];
	return min( min(m1,m2), min(m3,m4) );
}


int main(){
	int T;
	int M;
	int x1,y1,x2,y2;
	scanf("%d",&T);
	while(T--){
		scanf("%d",&N);
		for(int i=1; i<=N; i++) {
			for(int j=1; j<=N; j++) {
				scanf("%d",&val[i][j]);
			}
		}
		initRMQ(N,N);
		scanf("%d",&M);
		while(M--)	{
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
			printf("%d\n",RMQ(x1,x2,y1,y2));
		}
	}
	return 0;
}

抱歉!评论已关闭.