2019牛客暑期多校训练营(第八场)A All-one Matrices(单调栈,01矩阵)

  • 题意:在一个01矩阵中找全为1且不为其他矩形完全覆盖的矩形的个数
  • 思路:上下左右边界确定一个矩形。首先预处理01矩阵,例如:
3 4
0111
1110
0101

处理完为:

0 1 1 1
1 2 2 0
0 3 0 1

这个新得到的矩阵为(x,y)上面有多少连续的1
然后枚举每一行,对每一列用单调栈求矩形的左右边界,矩阵的上边界即是该点的大小,现在只需要判断这个矩形能不能向下延申,如果可以则说明他可以被另一个更大的矩形覆盖,判断能不能向下延申可以预处理每一行的前缀和,如果这行左右边界之间的和加上这一行左右边界之间的距离等于下一行左右边界之间的和 则说明可以向下延申。最后因为这样计算可能出现重复矩形,例如:

1 4
1111

这样的情况会求出4个矩形,所以需要去重,用map标记左右边界去重的化会超时,可以用单调栈来判断,维护一个单调递增的栈,每次判断,如果当前点的值等于栈顶的值则直接跳过不计算。

  • 代码:
#include<bits/stdc++.h>
using namespace std;
//#define int long long
char g[3005][3005];
int e[3005][3005];
int sumr[3005][3005];//行 
int n,m;
int l[3005],r[3005];
int init(int x,int y){
	if(x<1) return 0;
	if(g[x][y]=='1') e[x][y]=1;
	init(x-1,y);
	if(e[x][y]) e[x][y]+=e[x-1][y];
	return e[x][y]; 
}

int ans=0;
void found(int x){
	stack<int> s;
	while(s.size()) s.pop();
	for(int i=1;i<=m;i++){//左边界,单调增 
		while(s.size()&&e[x][s.top()]>=e[x][i]){
			s.pop();
		}
		if(s.empty()) l[i]=1;
		else l[i]=s.top()+1;
		s.push(i);
	}
	while(!s.empty()) s.pop();
	for(int i=m;i>=1;i--){
		while(s.size()&&e[x][s.top()]>=e[x][i]) s.pop();
		if(s.empty()) r[i]=m;
		else r[i]=s.top()-1;
		s.push(i);
	}
//	printf("\n");
//	for(int i=1;i<=m;i++){
//       	printf("%d ",l[i]);
//	}
//	printf("\n");
//	for(int j=1;j<=m;j++){
//		printf("%d ",r[j]);
//	}
//
//	printf("\n");
	while(s.size()) s.pop();
	
	for(int i=1;i<=m;i++){
		if(!e[x][i]) {
			while(!s.empty()) s.pop();
			continue;
		}	
		while(s.size()&&e[x][i]<e[x][s.top()]) s.pop();//单调递增 
		
		if(!s.empty()&&e[x][s.top()]==e[x][i]) continue;
		
   // 	printf("###%d %d %d\n",sumr[x][ r[i] ]-sumr[x][ l[i]-1 ],r[i]-l[i]+1,sumr[x+1][ r[i] ]-sumr[x+1][ l[i]-1 ]);

		if(sumr[x][ r[i] ]-sumr[x][ l[i]-1 ]+ r[i]-l[i]+1 ==sumr[x+1][ r[i] ]-sumr[x+1][ l[i]-1 ]) continue;
		ans++;
		s.push(i);
	}
//	printf("%d\n",ans);
}
int main(){
	
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",g[i]+1);
	}
	for(int i=1;i<=m;i++){
		init(n,i);
	}
//	for(int i=1;i<=n;i++){
//		for(int j=1;j<=m;j++){
//			printf("%d ",e[i][j]);
//		}
//		printf("\n");
//	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			sumr[i][j]=sumr[i][j-1]+e[i][j];
		}
	}
	for(int i=1;i<=n;i++)
		found(i);
	printf("%d\n",ans);
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值