19牛客暑期多校第二场H(单调栈)

求只有0和1的矩阵中第二大的全1子矩阵的1的个数

题解:单调栈求最大子矩阵 与这道题一样(板子)https://www.cnblogs.com/Kv-Stalin/p/9164705.html(特别详细),唯一不同就是最后要求的结果不同,所以有了代码中的去重部分,因为在处理每一行通过单调栈算矩形面积时,会重复算一个矩形面积好多次放在优先队列里,为避免这个才有了去重部分。

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<stack>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=1005;
int a[maxn][maxn];
int c[maxn][maxn];
int maxx,cnt;
int n,m;
int fun(int x,int y){
	if(x>n) return 0;
	if(c[x][y]==1){
	    a[x][y]=1;
	}
	fun(x+1,y);
	if(a[x][y])
	a[x][y]+=a[x+1][y];
	return a[x][y];	
}
int l[maxn];
int r[maxn];
priority_queue<int>q;
void gofun(int x){
	stack<int> s;
	for(int i=0;i<1005;i++)
		l[i]=r[i]=0;
	while(!s.empty()) s.pop();
	for(int i=1;i<=m;i++){//单增栈 找左边第一个小于的数 
		while(!s.empty()&&a[x][s.top()]>=a[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.empty()&&a[x][s.top()]>=a[x][i]) s.pop();
		if(s.empty()) r[i]=m;
		else r[i]=s.top()-1;
		s.push(i);	
	}
	for(int i=1;i<=m;i++){
		int area=a[x][i]*(r[i]-l[i]+1);
		if(area>maxx){//去重 
	       cnt=0;
	       maxx=area;
		   for(int j=l[i];j<=r[i];j++){//遍历同底
		   	    if(a[x][i]==a[x][j])//找同高
		   	  	cnt++;
		   }
		}
		q.push(area);
		q.push(max(a[x][i]*(r[i]-l[i]),(a[x][i]-1)*(r[i]-l[i]+1)));
		//这里直接把每个矩形的去掉一列或去掉一行的矩形面积算出来放优先队列
	}
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=m;j++){
    		char ch;
    		cin>>ch;
    		if(ch=='1') c[i][j]=1;
    		else c[i][j]=0;
		}
	}
	for(int i=1;i<=m;i++){
		fun(1,i);
	}
	for(int i=1;i<=n;i++){
		gofun(i);
	}
	while(!q.empty()){
		q.pop();
		cnt--;
		if(cnt==0) break;
	} 
	if(!q.empty()) cout<<q.top()<<endl;
	else cout<<0<<endl;
	
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值