[题目](https://www.luogu.org/problem/P1387)
先看完题目。
本题有两种解法
法1 前缀和
法2 DP
方法一
这道题和谋道前缀和的题目十分类似,那道题的名字叫 激光炸弹 。
那如何用前缀和判定正方形呢
先求出这个矩阵的前缀和
公式如下
f[i][j]=f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j];
简单模拟一下就可以理解。
怎么用呢
有了这个二维前缀和就可以求出矩阵里任意大小方形里包含元素的和,当然也包括
正方形。
由于矩阵里的数只有0和1;
那么当一个正方形里都是一时,这个正方形里的数的和就会等于这个正方形的面积
用这个性质就可以判定正方形了。
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[1000][1000],ans[1000][1000];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>f[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1]+f[i][j];
int l=min(m,n),res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int sum=0;
for(int k=1;k<=l;k++){
if(i<k&&j<k) continue;
sum=ans[i][j]-ans[i-k][j]-ans[i][j-k]+ans[i-k][j-k];
if(sum==k*k)
res=max(res,k);
}
}
cout<<res;
return 0;
}
DP
设ans[i][j]为以(i,j)为右下角的正方形的最大边长
这个方程就可以借鉴求二维前缀和的方程
ans[i][j]的值必定和它周围的三个位置的值有关
从图中可以观察得出
if(f[i][j])
ans[i][j]=min(ans[i-1][j],min(ans[i-1][j-1],ans[i][j-1]))+1;
上代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int f[110][110];
int ans[110][110];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>f[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(f[i][j])
ans[i][j]=min(ans[i-1][j],min(ans[i-1][j-1],ans[i][j-1]))+1;
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
res=max(ans[i][j],res);
cout<<res;
return 0;
}