题目描述
你有一个 n × m n \times m n×m 的包含 0 0 0 和 1 1 1 的矩阵,从里面找一个最大的正方形保证其中的每个数都为 0 0 0。
输入格式
第一行输入两个数
n
,
m
n,m
n,m,表示矩阵的长和宽。
接下来
n
n
n 行,每行
m
m
m 个整数,表示矩阵的每个数据。
输出格式
输出一个整数,表示最大的边长。
数据范围
对于
10
%
10\%
10% 的数据,
1
≤
n
,
m
≤
20
1 \le n,m \le 20
1≤n,m≤20。
对于
100
%
100\%
100% 的数据,
1
≤
n
,
m
≤
1000
1 \le n,m \le 1000
1≤n,m≤1000
样例
输入:
4 4
1 0 0 0
0 0 0 1
1 0 0 1
0 0 1 0
输出:
2
思路
首先,我们可以想到直接暴力枚举矩阵,判断矩阵内的数是否都为
0
0
0,并更新答案。
这样一定会超时。
我们可以考虑使用二维前缀和进行优化,这样就能优化到 Θ ( n × m × min ( n , m ) ) \Theta(n \times m \times \min(n, m)) Θ(n×m×min(n,m)) 的复杂度了。
关于具体的二维前缀和做法,我们可以画一下图:
要计算 s u m i , j sum_{i,j} sumi,j 的值,我们会从最接近它的 s u m i − 1 , j sum_{i - 1, j} sumi−1,j 转移过来,但是差了 j j j 一列,还要加上 s u m i , j − 1 sum_{i, j - 1} sumi,j−1。这时我们发现重复了,因此减去 s u m i − 1 , j − 1 sum_{i - 1, j - 1} sumi−1,j−1。最后加上 a i , j a_{i,j} ai,j。
因此, s u m i , j = s u m i − 1 , j + s u m i , j − 1 − s u m i − 1 , j − 1 + a i , j sum_{i,j} = sum_{i - 1, j} + sum_{i, j - 1} - sum_{i - 1, j - 1} + a_{i, j} sumi,j=sumi−1,j+sumi,j−1−sumi−1,j−1+ai,j。
对于计算 x 1 , y 1 x1,y1 x1,y1 到 x 2 , y 2 x2,y2 x2,y2 的值,可以仿照上面推导一下,在此就不在推导。
代码
#include<bits/stdc++.h>
using namespace std;
int n, m;
int a[1010][1010];//原数组
int sum[1010][1010];//前缀和
int main(){
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
scanf("%d", &a[i][j]);
}
}
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= m; ++ j){
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
}
}
int ans = 0;
//枚举 i,j,即左上角的行数和左下角的行数,再枚举左下角的列数,判断正方形的和是否为 0 即可
for(int i = 1; i <= n; ++ i){
for(int j = i; j <= n; ++ j){
int h = j - i + 1;
for(int k = 1; k <= m; ++ k){
int t = k + h - 1;
if(t > m){
break;
}
int u = sum[j][t] - sum[i - 1][t] - sum[j][k - 1] + sum[i - 1][k - 1];
//更新答案
if(u == 0){
ans = max(ans, h);
}
}
}
}
printf("%d", ans);
return 0;
}