LeetCode-1277统计全为 1 的正方形子矩阵
题目:给你一个 m * n
的矩阵,矩阵中的元素不是 0
就是 1
,请你统计并返回其中完全由 1
组成的 正方形 子矩阵的个数。
这是一个动态规划问题,有一些值得注意的地方。动态规划的思想就是将当前问题,拆成子问题,并且由子问题的值组合得到当前问题的值。这样我们就可以通过遍历小问题,得到我们想要的大问题的值。通常小问题的值,是很容易求得的。
一般来说,我们经常会保存最优值来组合得到更大问题的值。在这个问题中,最优值对求解更大问题的值却没有帮助,因此我们需要转换思路,观察具体矩阵可以得到一个递推关系。
例如:
输
入
矩
阵
[
0
,
1
,
1
1
,
1
,
1
1
,
1
,
1
]
最
优
值
矩
阵
[
0
,
1
,
1
1
,
3
,
6
1
,
6
,
11
]
输入矩阵 \begin{bmatrix} 0,1,1\\1,1,1\\1,1,1 \end{bmatrix}\\ 最优值矩阵 \begin{bmatrix} 0,1,1\\1,3,6\\1,6,11 \end{bmatrix}\\
输入矩阵⎣⎡0,1,11,1,11,1,1⎦⎤最优值矩阵⎣⎡0,1,11,3,61,6,11⎦⎤
假设输入矩阵是
A
A
A,我们希望能找到一个
S
[
i
]
[
j
]
S[i][j]
S[i][j]与小问题最优值之间的关系
[
∗
∗
∗
∗
∗
∗
∗
∗
0
]
[
∗
∗
∗
∗
∗
∗
∗
∗
1
]
\begin{bmatrix} ***\\***\\**0 \end{bmatrix} \begin{bmatrix} ***\\***\\**1 \end{bmatrix}\\
⎣⎡∗∗∗∗∗∗∗∗0⎦⎤⎣⎡∗∗∗∗∗∗∗∗1⎦⎤
假设
A
[
i
]
[
j
]
=
0
A[i][j]=0
A[i][j]=0,则容易得知:
S
[
i
]
[
j
]
=
S
[
i
]
[
j
−
1
]
+
S
[
i
−
1
]
[
j
]
−
S
[
i
−
1
]
S
[
j
−
1
]
S[i][j]=S[i][j-1]+S[i-1][j]-S[i-1]S[j-1]
S[i][j]=S[i][j−1]+S[i−1][j]−S[i−1]S[j−1]
可是当 A [ i ] [ j ] = 1 A[i][j]=1 A[i][j]=1时,如何找到 S [ i ] [ j ] S[i][j] S[i][j]与子问题之间的关系。
相比于包含 A [ i ] [ j ] = 0 A[i][j]=0 A[i][j]=0,这里多了包含 A [ i ] [ j ] A[i][j] A[i][j]的正方形数,可得到 S [ i ] [ j ] = S [ i ] [ j − 1 ] + S [ i − 1 ] [ j ] − S [ i − 1 ] S [ j − 1 ] + C o u n t ( c o n t a i n ( A [ i ] [ j ] ) ) S[i][j]=S[i][j-1]+S[i-1][j]-S[i-1]S[j-1]+Count(contain(A[i][j])) S[i][j]=S[i][j−1]+S[i−1][j]−S[i−1]S[j−1]+Count(contain(A[i][j]))。如何计算包含 A [ i ] [ j ] A[i][j] A[i][j]的正方形数?如果暴力搜索,那么将会产生 O ( m i n ( i , j ) 2 ) O(min(i,j)^2) O(min(i,j)2)的复杂度。从最优值中能否找到关系。笔者试了挺久。并没有找到合适的关系。
但是我们找到了问题的关键所在,计算包含 A [ i ] [ j ] A[i][j] A[i][j]的正方形数。假设我们已知子问题的包含 A [ m ] [ n ] , m < i , n < j A[m][n],m<i,n<j A[m][n],m<i,n<j的正方形数 B [ m ] [ n ] B[m][n] B[m][n](同时也是最大正方形边长)。那么问题就好解决了。
如图所示:
B
[
m
,
n
]
=
[
∗
,
∗
,
∗
∗
,
1
,
2
∗
,
2
,
?
]
可
以
由
此
写
出
部
分
矩
阵
[
∗
,
1
,
1
1
,
1
,
1
1
,
1
,
1
]
可
知
B
[
i
,
j
]
=
2
B[m,n]=\begin{bmatrix} *,*,*\\*,1,2\\*,2,? \end{bmatrix}\\ 可以由此写出部分矩阵 \begin{bmatrix} *,1,1\\1,1,1\\1,1,1 \end{bmatrix}\\ 可知B[i,j]=2
B[m,n]=⎣⎡∗,∗,∗∗,1,2∗,2,?⎦⎤可以由此写出部分矩阵⎣⎡∗,1,11,1,11,1,1⎦⎤可知B[i,j]=2
如下面两张图所示:包含 A [ i ] [ j ] A[i][j] A[i][j]的正方形数(最大正方形边长)实际上为 min ( B [ i − 1 , j ] , B [ i , j − 1 ] , B [ i − 1 , j − 1 ] ) + 1 \min(B[i-1,j],B[i,j-1],B[i-1,j-1])+1 min(B[i−1,j],B[i,j−1],B[i−1,j−1])+1。
则得到递推方程:
B
[
i
,
j
]
=
{
0
A[i,j]=0
min
(
B
[
i
−
1
,
j
]
,
B
[
i
,
j
−
1
]
,
B
[
i
−
1
,
j
−
1
]
)
+
1
A[i,j]=1
则
所
有
正
方
形
数
目
为
:
∑
i
∈
B
B
[
i
,
j
]
B[i,j]= \begin{cases} 0& \text{A[i,j]=0}\\ \min(B[i-1,j],B[i,j-1],B[i-1,j-1])+1& \text{A[i,j]=1} \end{cases}\\ 则所有正方形数目为:\\ \sum_{i\in B}B[i,j]
B[i,j]={0min(B[i−1,j],B[i,j−1],B[i−1,j−1])+1A[i,j]=0A[i,j]=1则所有正方形数目为:i∈B∑B[i,j]
代码实现如下:
int countSquares(vector<vector<int>>& matrix) {
if(matrix.size()==0) return 0;
int m = matrix.size();
int n = matrix[0].size();
int sum = 0;
for(int i = 0;i < m;++i) sum+=matrix[i][0];
for(int j = 0;j < n;++j) sum+=matrix[0][j];
sum-=matrix[0][0];
for(int i = 1;i <m;++i){
for(int j = 1;j < n;++j){
if(matrix[i][j]==1){
matrix[i][j] = min(matrix[i-1][j],matrix[i][j-1]);
matrix[i][j] = min(matrix[i-1][j-1],matrix[i][j]);
++matrix[i][j];
sum+=matrix[i][j];
}
}
}
return sum;
}
小结
对于动态规划问题,有时不一定需要找到最优值原问题与子问题的关系。采取另外的方式去寻找递推关系,或许会更好。