题目链接:https://ac.nowcoder.com/acm/contest/888/A
题意:给n×m的01矩阵,求出其中全为1的矩阵个数(不被其它全1矩阵包括)。
思路:和第二场多校的次大子矩阵类似。二维降一维,遍历行,以第i行为矩阵的底,通过单调栈得到矩阵向左向右能够扩展的范围 l[j]和r[j],然后关键在去重。有两种重复,第一种是同一行中的矩阵,可以用二维矩阵vis[l[j]][r[j]]标记此范围的矩阵已被记录。对于列的重复,是因为此时的矩阵能在下一行得到行扩展,所以预处理得到下一行的‘1’个数的前缀和num[j],通过判断num[r[j]-1]-num[l[j]]==r[j]-l[j]-1来判断是否能扩展到下一行,能的话当前不记录。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=3005; int n,m,ans; int a[maxn][maxn],stk1[maxn],stk2[maxn]; int l[maxn],r[maxn],num[maxn],vis[maxn][maxn]; char c[maxn][maxn]; int main(){ scanf("%d%d",&n,&m); for(register int i=1;i<=n;++i){ a[i][0]=a[i][m+1]=-1; scanf("%s",c[i]+1); for(register int j=1;j<=m;++j){ register int tmp=c[i][j]-'0'; if(tmp) a[i][j]=a[i-1][j]+1; } } stk1[0]=0,stk2[0]=m+1; for(register int i=1;i<=n;++i){ register int p1=1,p2=1; for(register int j=1;j<=m;++j){ while(a[i][stk1[p1-1]]>=a[i][j]) --p1; l[j]=stk1[p1-1]; stk1[p1++]=j; } for(register int j=m;j>=1;--j){ while(a[i][stk2[p2-1]]>=a[i][j]) --p2; r[j]=stk2[p2-1]; stk2[p2++]=j; } for(int j=1;j<=m;++j) num[j]=num[j-1]+(c[i+1][j]=='1'); for(register int j=1;j<=m;++j){ if(!a[i][j]) continue; if(vis[l[j]+1][r[j]-1]==i) continue; if(num[r[j]-1]-num[l[j]]==r[j]-l[j]-1) continue; ++ans; vis[l[j]+1][r[j]-1]=i; } } printf("%d ",ans); return 0; }