我给的是N x M
矩阵。对于从位置X
开始的长度为(a, b)
的子矩阵,我必须找到子矩阵中存在的最大元素。
我的方法:
for(i in range(a, a + x)) for(j in range(b, b + x)) max = max(max,A[i][j]) // N * M
一点进步:
1. Make a segment tree for every i in range(0, N)
2. for i in range(a, a + x) query(b, b + x) // N * logM
是否有更好的解决方案只有O(log n)复杂度?
答案 0 :(得分:5)
稀疏表算法方法
: - <O( N x M x log(N) x log(M)) , O(1)>
。
预计算时间 - O( N x M x log(N) x log(M))
查询时间 - O(1)
为了理解这种方法,你应该知道在一维中使用稀疏表算法找到RMQ。
我们可以使用2D稀疏表算法来查找范围最小查询。
我们在One Dimension中做了什么: -
我们使用动态编程为长度为2^k
的子数组预处理 RMQ 。我们将保留一个数组M[0, N-1][0, logN]
,其中M[i][j]
是从i
开始的子数组中最小值的索引。
为了计算M[i][j]
,我们必须在间隔的前半部分和后半部分中搜索最小值。很明显,小块的长度为2^(j – 1)
,因此用于计算的伪代码为: -
if (A[M[i][j-1]] < A[M[i + 2^(j-1) -1][j-1]])
M[i][j] = M[i][j-1]
else
M[i][j] = M[i + 2^(j-1) -1][j-1]
这里A
是存储值的实际数组。一旦我们对这些值进行了预处理,让我们展示如何使用它们来计算 RMQ(i,j)。我们的想法是选择两个完全覆盖区间[i..j]
的块,并找出它们之间的最小值。让k = [log(j - i + 1)]
。对于计算 RMQ(i,j),我们可以使用以下公式: -
if (A[M[i][k]] <= A[M[j - 2^k + 1][k]])
RMQ(i, j) = A[M[i][k]]
else
RMQ(i , j) = A[M[j - 2^k + 1][k]]
对于2维: -
类似地,我们也可以扩展2维度的规则,这里我们使用动态编程&amp; amp;来预处理长度为2^K, 2^L
的子矩阵的 RMQ 。保留一个数组M[0,N-1][0, M-1][0, logN][0, logM]
。其中M[x][y][k][l]
是子矩阵中最小值的索引,从[x , y]
开始,长度分别为2^K, 2^L
。
用于计算的伪代码M[x][y][k][l]
是: -
M[x][y][i][j] = GetMinimum(M[x][y][i-1][j-1], M[x + (2^(i-1))][y][i-1][j-1], M[x][y+(2^(j-1))][i-1][j-1], M[x + (2^(i-1))][y+(2^(j-1))][i-1][j-1])
此处GetMinimum
函数将从提供的元素返回最小元素的索引。现在我们已预处理,让我们看看如何计算 RMQ(x,y,x1,y1)。这里[x, y]
子矩阵的起点和[x1, y1]
表示子矩阵的终点意味着子矩阵的右下点。在这里,我们必须选择四个完全覆盖[x, y, x1, y1]
的子矩阵块,并找到它们中的最小值。让k = [log(x1 - x + 1)]
&amp; l = [log(y1 - y + 1)]
。对于计算 RMQ(x,y,x1,y1),我们可以使用以下公式: -
RMQ(x, y, x1, y1) = GetMinimum(M[x][y][k][l], M[x1 - (2^k) + 1][y][k][l], M[x][y1 - (2^l) + 1][k][l], M[x1 - (2^k) + 1][y1 - (2^l) + 1][k][l]);
以上逻辑的伪代码: -
// remember Array 'M' store index of actual matrix 'P' so for comparing values in GetMinimum function compare the values of array 'P' not of array 'M'
SparseMatrix(n , m){ // n , m is dimension of matrix.
for i = 0 to 2^i <= n:
for j = 0 to 2^j <= m:
for x = 0 to x + 2^i -1 < n :
for y = 0 to y + (2^j) -1 < m:
if i == 0 and j == 0:
M[x][y][i][j] = Pair(x , y) // store x, y
else if i == 0:
M[x][y][i][j] = GetMinimum(M[x][y][i][j-1], M[x][y+(2^(j-1))][i][j-1])
else if j == 0:
M[x][y][i][j] = GetMinimum(M[x][y][i-1][j], M[x+ (2^(i-1))][y][i-1][j])
else
M[x][y][i][j] = GetMinimum(M[x][y][i-1][j-1], M[x + (2^(i-1))][y][i-1][j-1], M[x][y+(2^(j-1))][i-1][j-1], M[x + (2^(i-1))][y+(2^(j-1))][i-1][j-1]);
}
RMQ(x, y, x1, y1){
k = log(x1 - x + 1)
l = log(y1 - y + 1)
ans = GetMinimum(M[x][y][k][l], M[x1 - (2^k) + 1][y][k][l], M[x][y1 - (2^l) + 1][k][l], M[x1 - (2^k) + 1][y1 - (2^l) + 1][k][l]);
return P[ans->x][ans->y] // ans->x represent Row number stored in ans and similarly ans->y represent column stored in ans
}
答案 1 :(得分:0)
AFAIK,因为矩阵没有顺序,所以不能有O(logn方法)。但是,如果您有一个订单,使得每行按从左到右的升序排序,并且每列按从上到下的顺序排序,那么您就知道A [a + x] [b + x](右下角的单元格) submatrix)是该子矩阵中最大的元素。因此,一旦矩阵被排序,找到最大值需要O(1)时间。但是,对矩阵进行排序(如果尚未排序)将花费O(NxM log {NxM})
答案 2 :(得分:0)
以下是c ++中的示例代码,用于@Chapta给出的伪代码,正如某些用户所要求的那样。
int M[1000][1000][10][10];
int **matrix;
void precompute_max(){
for (int i = 0 ; (1<<i) <= n; i += 1){
for(int j = 0 ; (1<<j) <= m ; j += 1){
for (int x = 0 ; x + (1<<i) -1 < n; x+= 1){
for (int y = 0 ; y + (1<<j) -1 < m; y+= 1){
if (i == 0 and j == 0)
M[x][y][i][j] = matrix[x][y]; // store x, y
else if (i == 0)
M[x][y][i][j] = max(M[x][y][i][j-1], M[x][y+(1<<(j-1))][i][j-1]);
else if (j == 0)
M[x][y][i][j] = max(M[x][y][i-1][j], M[x+ (1<<(i-1))][y][i-1][j]);
else
M[x][y][i][j] = max(M[x][y][i-1][j-1], M[x + (1<<(i-1))][y][i-1][j-1], M[x][y+(1<<(j-1))][i-1][j-1], M[x + (1<<(i-1))][y+(1<<(j-1))][i-1][j-1]);
// cout << "from i="<<x<<" j="<<y<<" of length="<<(1<<i)<<" and length="<<(1<<j) <<"max is: " << M[x][y][i][j] << endl;
}
}
}
}
}
int compute_max(int x, int y, int x1, int y1){
int k = log2(x1 - x + 1);
int l = log2(y1 - y + 1);
// cout << "Value of k="<<k<<" l="<<l<<endl;
int ans = max(M[x][y][k][l], M[x1 - (1<<k) + 1][y][k][l], M[x][y1 - (1<<l) + 1][k][l], M[x1 - (1<<k) + 1][y1 - (1<<l) + 1][k][l]);
return ans;
}
此代码首先预先计算2维稀疏表,然后在恒定时间内查询它。 附加信息:稀疏表存储最大元素而不是最大元素的索引。