题意:给出N*M的01矩阵,求矩阵个数,满足矩阵内全是‘1’,,而且被至少一个’0‘围住。(假设边界外是‘0’。(N,M<3000)
思路:这类问题,一般解决就是两个方向:
A:压缩一维,即枚举上下边界,然后复杂度O(N^3);
B:广告牌问题,即枚举下边界s,那么久可以看成s为底的一些高楼,再高楼上布置广告牌,复杂度O(N^2)。
显然这里是B类问题。 那么B方法来做,还有两个问题需要处理:
1:去重。 方法应该有很多,这里用的是:(L,R)出现次数的唯一的,直接vis[L][R]即可。
2:如果底下全是‘1’,则不合法。
去重的话,加个vis数组,给个时间戳就可以了。 不合法的话,等效于底下全是1,预处理底下的前缀和即可。
(我也不知道为什么过得这么少啊...
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=3010; char c[maxn][maxn]; int M,h[maxn][maxn],ans,L[maxn],R[maxn],num[maxn],vis[maxn][maxn]; int q[maxn],head; void get(int t) { head=0; q[++head]=0; rep(j,1,M) { while(head>0&&h[t][j]<=h[t][q[head]]) head--; L[j]=q[head]+1; q[++head]=j; } head=0; q[++head]=M+1; for(int j=M;j>=1;j--) { while(head>0&&h[t][j]<=h[t][q[head]]) head--; R[j]=q[head]-1; q[++head]=j; } rep(i,1,M) num[i]=num[i-1]+(c[t+1][i]=='1'); rep(i,1,M){ if(h[t][i]==0) continue; if(vis[L[i]][R[i]]==t) continue; //去重 if(num[R[i]]-num[L[i]-1]==R[i]-L[i]+1) continue; //去不合法 ans++; vis[L[i]][R[i]]=t; } } int main() { int N; scanf("%d%d",&N,&M); rep(i,1,N) scanf("%s",c[i]+1); rep(i,1,N) rep(j,1,M){ if(c[i][j]=='0') h[i][j]=0; else h[i][j]=h[i-1][j]+1; } rep(i,1,N) get(i); printf("%d ",ans); return 0; }