题意:给定一个由0和1组成的n*m矩阵,现在要求找出有几个子矩阵符合下列要求。1.矩形为正方形,且边长最小为2;2.矩阵的四条边上数字都为1;3.矩阵内部中0的个数a和1的个数b(不包括边界),符合|a-b|<=1。
题解:先预处理一些数据,再枚举所有点,计算所枚举的点作为左上角的符合矩阵个数。
其中预处理:r[i][j]表示(i,j)右边有几个连续的1,c[i][j]表示(i,j)点下面有几个连续的1,f[i][j]表示(i,j)与右下角所成矩形的和。
预处理的好处:
1)sum=f[i+1][j+1]-f[i+k-1][j+1]-f[i+1][j+k-1]+f[i+k-1][j+k-1];直接求出(i,j)为左上角,k为边长的矩阵中间部分的1的个数,用于banding条件3
2)t=min(r[i][j],c[i][j]);找出以(i,j)为坐上角的矩阵最长可能符合边长,以及if(r[i+k-1][j]<k||c[i][j+k-1]<k)continue;判断条件2.
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <vector> #include <map> #include <queue> #include <iostream> #include <algorithm> using namespace std; const int maxn=303; int e[maxn][maxn]; int r[maxn][maxn],c[maxn][maxn];//r[i][j]表示(i,j)右边有几个连续的1,c[i][j]表示(i,j)点下面有几个连续1 int f[maxn][maxn];//f[i][j]表示(i,j)与右下角所成矩形的和 int main() { int T; scanf("%d",&T); while(T--) { int n,m,i,j,k,d,sum,t; scanf("%d%d",&n,&m); for(i=0;i<n;i++) { for(j=0;j<m;j++) scanf("%d",&e[i][j]); } memset(r,0,sizeof(r)); memset(c,0,sizeof(c)); //计算r[i][j] for(i=0;i<n;i++) { d=0; for(j=m-1;j>=0;j--) { if(e[i][j]==1)d++; else d=0; r[i][j]=d; } } //计算c[i][j]; for(j=0;j<m;j++) { d=0; for(i=n-1;i>=0;i--) { if(e[i][j]==1)d++; else d=0; c[i][j]=d; } } //计算f[i][j]; for(i=n-1;i>=0;i--) { for(j=m-1;j>=0;j--) { f[i][j]=f[i][j+1]+f[i+1][j]-f[i+1][j+1]+e[i][j]; } } /*printf("***********\n"); for(i=0;i<n;i++) { for(j=0;j<m;j++) printf("%d ",f[i][j]); printf("\n"); } printf("*************\n");*/ //计算符合条件的方格数 int ans=0; for(i=0;i<n-1;i++) { for(j=0;j<m-1;j++) { if(e[i][j]==0)continue; t=min(r[i][j],c[i][j]); //if(i==0&&j==0)printf("*%d\n",t); for(k=2;k<=t;k++) { //if(k==4)printf("**%d %d\n",r[i+k-1][j],c[i][j+k-1]); if(r[i+k-1][j]<k||c[i][j+k-1]<k)continue; sum=f[i+1][j+1]-f[i+k-1][j+1]-f[i+1][j+k-1]+f[i+k-1][j+k-1]; //if(k==4) //printf("***%d %d %d %d\n",f[i+1][j+1],f[i+k-1][j+1],f[i+1][j+k-1],f[i+k-1][j+k-1]); if(abs(2*sum-(k-2)*(k-2))<=1)ans++; } } } printf("%d\n",ans); } return 0; }