51Nod - 1158 全是1的最大子矩阵 单调栈

题目链接



题意:

给出1个M*N的矩阵M1,里面的元素只有0或1,找出M1的一个子矩阵M2,M2中的元素只有1,

并且M2的面积是最大的。输出M2的面积。



思路:

先说一种单调栈的做法,

方法是:

1、先将0/1矩阵读入x,对每一个非零元素x[i][j],将其更新为:在本行,它前面的连续的1的个数+1

(+1表示算入自身)比如,若某一行为0 1 1 0 1 1 1,则更新为0 1 2 0 1 2 3

2、对每一个非零元素x[i][j],在第j列向上和向下扫描,直到遇到比自身小的数,若扫描了y行,则得

到一个大小为x[i][j]*(y+1)的全1子矩阵(+1表示算入自身所在行)

  比如,若某一列为[0 3 4 3 5 2 1]'(方便起见,这里将列表示成一个列向量),我们处理这一列的

第4个元素,也就是3,它向上可以扫描2个元素,向下可以扫描1个元素,于是得到一个4×3的全1子矩阵。

3、在这些数值中取一个最大的。

思想大概如下图所示(空白处的0没有标出)

对照步骤2中给出的例子,蓝色的箭头表示向上向下扫描,黑色的框表示最终得到的全1子矩阵

这样做为什么是对的?

想一想,对那个最大的全1子矩阵,用这种方法能不能找到它呢?——肯定可以。

一个最大全1子矩阵,肯定是四个边界中的每一个都不能再扩展了,如下图

假设图中全1子矩阵就是最大子矩阵,则左边界左侧那一列肯定有一个或多个0(否则就可以向

左边扩展一列,得到一个更大的全1矩阵)

对其他3个边界有类似的情况。

//复杂度 O(n2)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int m,n,a;
int f[555][555];
int l[555],r[555];
int main(){

	scanf("%d %d",&m,&n);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a);
			if(a)
			f[i][j]=f[i][j-1]+1;
			else
			f[i][j]=0;
		}
	}
	int ans=0;
	
	for(int i=1;i<=n;i++)
	{
		stack<int>s,st;
		l[1]=1,r[m]=m;
		for(int j=1;j<=m;j++)
		{
			if(s.empty())
			{
				s.push(j);
			}
			else
			{
				if(f[s.top()][i]>=f[j][i])
				{
				while(!s.empty()&&f[s.top()][i]>=f[j][i])
				{
					s.pop();
				}
				if(s.empty())
				{
					l[j]=1;
				}
				else
				{
					l[j]=s.top()+1;
				}
				s.push(j);
				}
				else
				{
					l[j]=j;
					s.push(j);
				}
			}
		}
		for(int j=m;j>=1;j--)
		{
			if(st.empty())
			{
				st.push(j);
			}
			else
			{
				if(f[st.top()][i]>=f[j][i])
				{
				
				while(!st.empty()&&f[st.top()][i]>=f[j][i])
				{
					st.pop();
				}
				if(st.empty())
				{
					r[j]=m;
				}
				else
				{
					r[j]=st.top()-1;
				}
				st.push(j);
		    	}
		    	else
		    	{
		    		r[j]=j;
		    		st.push(j);
				}
			}
		}
		for(int j=1;j<=m;j++)
		{
			ans=max(ans,(r[j]-l[j]+1)*f[j][i]);
		}
	}
	printf("%d\n",ans);
	return 0;
}




       

再说一种我自己的做法,可以预处理出每一列的前缀和,这样我们就可以枚举两行i,j.将i到j的

每一列和和压缩成一个数b【k】,从而变成一个一维数组,然后比较b【k】中1的个数是否等于高,

从而判断是否全是1,并记录最大的矩阵.

     


//复杂度On3

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
int n,m,a;
int sum[555][555],b[555];
int main(){
	
	scanf("%d %d",&m,&n);
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a);
			sum[i][j]=sum[i-1][j]+a;	
		}
	}
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=i;j<=m;j++)
		{
			for(int k=1;k<=n;k++)
			b[k]=sum[j][k]-sum[i-1][k];
			int cnt=0;
			for(int k=1;k<=n;k++)
			{
				if(b[k]==(j-i+1))
				cnt++;
				else
				{
					ans=max(ans,cnt*(j-i+1));
					cnt=0;
				}
			}
			ans=max(ans,cnt*(j-i+1));//这里记得需要最后算一次,因为上面的循环最后一列不一定不满足,哪样就少算了一个
		}
	}
	printf("%d\n",ans);
	return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值