题目描述
在一个 的只包含 和 的矩阵里找出一个不包含 的最大正方形,输出边长。
输入格式
输入文件第一行为两个整数 ( ),接下来 行,每行 个数字,用空格隔开, 或 。
输出格式
一个整数,最大正方形的边长。
样例
样例输入
4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1
样例输出
2
我不知道前缀和算不算正解,但既然 了就写一篇题解。
正所谓暴力出奇迹. . . . . .其实我是看到 才想到暴力的,但 在我意料之外,吸氧之后就 。
前置知识
前缀和 ——— 从一维到二维
基本概念
一维前缀和,在一个一维数组中任意区间内元素之和。
二位前缀和,在一个二维矩阵中任意矩阵内元素之和。
实现方式
对于一维前缀和,用一个一维数组记录每一位 到该位元素的和,即 。
在询问时可以以 的时间复杂度输出答案,即 ,其中 表示区间左边界, 表示区间右边界。举个例子方便理解:
a[0-10] ={ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10}
sum[0-10]={ 0, 1, 3, 6,10,15,21,28,36,45,55}
a数组为初始数组
sum[i]记录a数组中区间[1,i]内所有元素之和
如果题目询问的左边界并不是1,就用sum[L]减去左边界以前的所有元素和,即减sum[L-1]
比如[3,5]的区间和为:sum[5]-sum[3-1]=15-3=12,3+4+5=12,完全正确
代码实现如下:
scanf("%d%d",&n,&q);
//n表示a数组长度,q为询问次数
for(int i=1;i<=n;i++){
scanf("%d",a[i]);
sum[i]=sum[i-1]+a[i];
//在上一个前缀和的基础上加入刚输入的数
}
for(int i=1;i<=q;i++){
scanf("%d%d",&L,&R);
printf("%d\n",sum[R]-sum[L-1]);
}
对于二维前缀和, 用一个二维数组记录矩阵中每一点作为矩阵右下角, 作为矩阵左上角构成的小矩阵中所有元素之和,下面用一张图表示:
图中绿色阴影中的部分即为 所覆盖中的小矩阵,同样的:如何询问的矩阵左上角并不在 的位置该如何解决?考虑仍然用相减的方法, 的时间复杂度输出答案。
下面画图帮助理解:
上图为求左上角为 右下角为 构成小矩阵内元素和的计算划分图,可以表示为 。
对减去 做出解释:由于我们在求 和 时会求和两次 , 所以我们需要再减去一次 。
根据上面的运算推式子得出:,其中 为小矩阵左上角, 为小矩阵右上角。代码实现如下:
for(int i=1;i<=n;i++)
for(int j=1,x;j<=m;j++){
scanf("%d",&x);
//sum[][]记算前缀和
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
}
for(int i=1,xa,xb,ya,yb;i<=q;i++){
scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
printf("%d\n",sum[xb][yb]-sum[xa-1][yb]-sum[xb][ya-1]+sum[xa-1][ya-1]);
}
思路
题目让我们求一个不包含 的最大正方形,输出边长。
不包含 ,也就意味着这个正方形内部全是 ,那这个正方形的面积就是边长乘边长了。这个正方形内部但凡有一个 ,面积就会变小,那么要确定正方形内部有没有 ,直接在输入时求出前缀和,然后和正确的正方形面积比对一下不就好了吗?!正确的正方形面积指在内部没有 的情况下正方形的面积,即边长乘边长。
到这时本题唯一的难点就解决了,接下来就只需要考虑枚举的问题,最简单的就是枚举每个点,每次以这个点为正方形的左上角,然后再设一重循环枚举正方形的边长:
如果正方形的范围超出了矩阵,就结束这层循环
否则判断正方形内部是否有 ,如果没有就更新最大值
完整代码
#include<bits/stdc++.h>
using namespace std;
//快读,可加可不加
inline int read(){
int x;char ch;
while(true){
ch=getchar();
if(ch>='0'&&ch<='9') break;
}x=ch-'0';
while(true){
ch=getchar();
if(ch<'0'||ch>'9') break;
x=x*10+ch-'0';
}return x;
}
int main()
{
int n,m,maxn=0;n=read(),m=read();
int sum[n+1][m+1];
for(int i=1;i<=n;i++)
for(int j=1,x;j<=m;j++){
x=read();
//sum[][]记录前缀和
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
//k枚举正方形的边长
for(int k=1;k;k++){
int x=i+k-1,y=j+k-1;
//如果正方形的范围超出了矩阵,就结束这层循环
if(y<1||y>m||x<1||x>n) break;
if(k*k==sum[x][y]-sum[i-1][y]-sum[x][j-1]+sum[i-1][j-1])
maxn=max(maxn,k);
}
printf("%d",maxn);
return 0;
}
思路中关于前缀和的内容,本篇文章不做过多解释,想学习的的戳这里(此处给出的链接并不是本人的文章)