[前缀和]最大正方形

题目描述

你有一个 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 1n,m20
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 1000 1 \le n,m \le 1000 1n,m1000

样例

输入:

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} sumi1,j 转移过来,但是差了 j j j 一列,还要加上 s u m i , j − 1 sum_{i, j - 1} sumi,j1。这时我们发现重复了,因此减去 s u m i − 1 , j − 1 sum_{i - 1, j - 1} sumi1,j1。最后加上 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=sumi1,j+sumi,j1sumi1,j1+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;
} 
  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值