给定一个方形矩阵,每个单元格都是黑色或白色。设计一种算法,以查找最大子正方形,使得所有4个边界都是黑色。
我有一个O(n^2)的算法:
从左到右扫描每列,在每列中的每个单元格中,扫描每行以查找具有黑色边框的最大子正方形。
是否有更好的解决方案?
谢谢
给定一个方形矩阵,每个单元格都是黑色或白色。设计一种算法,以查找最大子正方形,使得所有4个边界都是黑色。
我有一个O(n^2)的算法:
从左到右扫描每列,在每列中的每个单元格中,扫描每行以查找具有黑色边框的最大子正方形。
是否有更好的解决方案?
谢谢
O(n^2)是可能的。我猜这是最优的,因为你有n^2个单元格。
请注意,任何正方形的左上角和右下角都位于同一条对角线上。
现在,如果我们可以在O(n)时间内处理每个对角线,我们将拥有一个O(n^2)时间复杂度的算法。
假设我们有一个左上角的候选者。我们可以计算其下方和右侧连续黑色单元格的数量,并取两者中的最小值,并称之为T。
对于右下角的候选者,我们可以计算其左侧和上方连续黑色单元格的数量,并取两者中的最小值,称之为B。
一旦我们有了两个数字T和B,我们就可以判断给定的左上角、右下角候选者是否实际上给出了一个带有所有黑色边框的正方形。
现在,通过四次遍历整个矩阵(如何?),可以在O(n^2)时间内为每个单元格计算出这两个数字。
所以让我们假设已经完成了这一步。
现在考虑一个对角线。我们的目标是在O(n)时间内找到一个沿着这个对角线的左上角、右下角对。
假设我们在一个数组T[1...m]中计算了T的值,其中m是对角线的长度。同样地,我们有一个数组B[1...m]。T[1]对应于对角线的左上端,T[m]对应于右下端。B也是如此。
现在我们按照以下方式处理对角线:对于每个左上角候选i,我们尝试找到一个右下角候选j,它将给出最大的正方形。请注意,j >= i。还要注意,如果(i,j)是一个候选者,则(i',j)不是一个候选者,其中i' > i。
请注意,如果T[i] >= j-i+1且B[j] >= j-i+1,则i和j形成一个正方形。
即T[i] +i - 1 >= j
和B[j] -j - 1 >= -i
。
因此,我们形成新的数组,使得TL[k] = T[k] + k -1
和BR[k] = B[k] -k - 1
。
因此,给定这两个新数组TL和BR以及一个i,我们需要回答以下查询:
什么是最大的j,使得TL[i] >= j且BR[j] >= -i?
现在假设我们能够处理BR的范围最大查询(可以在O(m)时间内完成)。
现在,在范围[i,TL[i]]中给定TL[i],我们找到BR的最大值,称之为BR[j1]。
如果BR[j1] >= -i,我们在范围 [j1, TL[i]] 内找到 BR 的最大值并继续这样做。
一旦我们找到(TL[i],BR[j])的候选者,我们可以忽略数组BR[1...j]以便未来的i。
这使我们能够以O(n)时间处理每个对角线,从而得到一个O(n^2)总算法。
我已经省略了很多细节并给出了一个草图,因为它已经太长了。请随意编辑并进行澄清。
呼。
我不知道为什么你能得到一个O(n^2)的算法。从数学上讲,这是不可能的。
假设你有一个NxN矩阵。你需要检查:
1. 大小为NxN的1个矩阵,
2. 大小为(N-1)x(N-1)的2x2矩阵,
3. 大小为(N-2)x(N-2)的3x3矩阵,
....
/* In a square matrix, where each cell is black or white.
* Design an algorithm to find the max sub-square such that all 4 borders are black. The right Java implementation based on a previous post.
*/
public int maxSubsquare(boolean[][] M){
int n = M.length;
maxsize=0;
checkDiag(M, 0, 0, n);
for (int i=1; i<n; i++){
checkDiag(M, i, 0, n);
checkDiag(M, 0, i, n);
}
return maxsize;
}
int maxsize;
void checkDiag(boolean[][] M, int i, int j, int n){
if (i>=n-maxsize || j>=n-maxsize) return;
int step = 0;
while (step<(n-Math.max(i, j))&& M[i][j+step]&&M[i+step][j]){
int s = step;
while (s>0){
if (M[i+s][j+step]&&M[i+step][j+s]) s--;
else break;
}
if (s==0)
maxsize = Math.max(maxsize, step+1);
step++;
}
if (step==0) checkDiag(M, i+step+1, j+step+1, n);
else checkDiag(M, i+step, j+step, n);
}
C++ 代码:
#include<iostream>
using namespace std;
int min(int a,int b,int c)
{
int min = a;
if(min > b)
min = b;
if(min > c)
min = c;
return min;
}
int main()
{
const int size = 5;
char a[size][size] = { {'b','b','b','b','w'},{'b','b','b','b','b'},{'b','b','b','b','b'},{'b','b','w','b','b'},{'b','w','w','b','b'} };
int arr[size+1][size+1];
// First row and First column of arr is zero.
for(int i=0;i<size+1;i++)
{
arr[0][i] = 0;
arr[i][0] = 0;
}
int answer = 0;
for(int i=0;i<size;i++)
{
for(int j=0;j<size;j++)
{
if(a[i][j] == 'w')
arr[i+1][j+1] = 0;
if(a[i][j] == 'b')
{
int minimum = min(arr[i][i],arr[i+1][j],arr[i][j+1]);
arr[i+1][j+1] = minimum + 1;
if( arr[i+1][j+1] > answer)
answer = arr[i+1][j+1];
}
}
}
for(int i=0;i<size+1;i++)
{
for(int j=0;j<size+1;j++)
{
cout<<arr[i][j]<<"\t";
}
cout<<endl;
}
cout<<answer<<endl;
return 0;
}