题意:

输入 n*m 的01矩阵
有多少个全1矩阵,不会被其他的全1矩阵覆盖

题目链接:

https://ac.nowcoder.com/acm/contest/888/A

题解:

单调栈+前缀和
对于每-一个格子(ij) , 记up[il[j]为其向上的连续的1的个数。
然后枚举每一行作为矩阵的底边所在行,从前往后枚举每一列 ,枚举时候,记录更新一个pos值,判断下一行该列为不为0, 方便为下面能否向下扩展做判断, 维护一个关于up[i][j]的单调上升的栈,对于栈中每一个up值,那么每当有元素退栈的时候,判断与后一个元素是否相等,相等则可以向右扩展,不进行任何操作, 栈内前一个元素为左边所能到达最远位置, 判断一个下一行该列的连续1所能到达最左边位置pos是否大于该位置, 是则说明不能向下扩展,我们可以得到一一个全1矩阵(i+up-1,sta[top-1] + 1)-(ij) ,且不能向上下左右扩展了。
时间复杂度: O(nm)

AC_code:

/* Algorithm: Author: anthony1314 Creat Time: Time Complexity: */

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e3+5;
int up[maxn][maxn], sta[maxn];//每个点的其向上的连续的1的个数 栈
char c[maxn][maxn];
int n, m;
int main() {
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i++){
		scanf("%s", c[i]+1);
	}
	for(int i = 1; i <= n; i++){//预处理 
		for(int j = 1; j <= m; j++){
			if(c[i][j] == '0'){
				up[i][j] = 0;
			}else {
				up[i][j] = up[i-1][j] + 1;
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		int pos = 0, top = 0;
		sta[++top] = 0;
		if(c[i][1] == '0'){//放入第一个元素 
			sta[top] = 1;
		}else {
			sta[++top] = 1;
		}
		for(int j = 1; j <= m; j++){
			if(j != 1){// 第一个已经如果栈了 不必入了 
				sta[++top] = j;
			}
			if(c[i+1][j] == '0' || i == n) {//判断下一行为不为0和是不是最后一行 为0和最后一行说明这是矩形下一行所能向左扩展最远位置 
				pos = j; 
			}
			while(top > 0 && up[i][sta[top]] >= up[i][j+1]){//栈顶元素 大于等于 下一个数 则退栈 
				if(top > 1 && sta[top-1] < pos && c[i][j] != '0' && up[i][sta[top]] > up[i][j+1]) {//判断退栈元素是否无法向右扩展 
					ans++;
				}
				top--;
			}
		}
		cout<<ans<<endl; 
	}
	cout<<ans<<endl; 
	return 0;
}