题意
给定一个 r 行 c 列的 01 矩阵,求至多包含一个1的正方形子矩阵的最大边长。
思路
使用前缀和维护矩阵,可以在 O ( 1 ) O( 1 ) O(1) 的时间计算任意子矩阵中 1 的个数。
枚举所有的点,对于点 ( i , j ) ( i , j ) (i,j),二分所有以该点为左上角的正方形子矩阵的边长,找到满足条件的最大正方形的边长。
总体复杂度为 O ( T ∗ n 2 l o g n ) O( T * n^2logn ) O(T∗n2logn),本道题目卡常,需要用到快读等算法,并对绝对时间进行极致的优化。
代码
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1010;
int r, c;
int g[maxn][maxn];
int s[maxn][maxn];
#define get(x1, y1, x2, y2) (s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1])
int bsearch_2(int x, int y, int l, int r){
while(l < r){
int mid = l + r + 1 >> 1;
if(get(x, y, x + mid -1, y + mid - 1) <= 1) l = mid;
else r = mid-1;
}
return l;
}
int read() {
char c; int num, f = 1;
while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
return f * num;
}
signed main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
int t; t = read();
while(t--){
r = read(); c = read();
for(int i = 1; i<=r; i++){
for(int j = 1; j<=c; j++){
g[i][j] = read();
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + g[i][j];
}
}
int ans = 0;
for(int i = 1; i<=r; i++){
for(int j = 1; j<=c; j++){
int max_r = min(r-i +1, c-j+1);
int l = bsearch_2(i, j, ans+1, max_r);
if(i+l-1<=r && j+l-1<=c && get(i, j, i+l-1, j+l-1) <= 1){
ans = max(ans, l);
}
}
}
printf("%d\n", ans);
}
return 0;
}