2019牛客暑期多校训练营(第二场) H Second Large Rectangle 【次大全1子矩阵和】【单调栈】

本文介绍了一个01矩阵中寻找第二大全1子矩阵的问题,并详细解释了如何利用单调栈进行优化,最终实现高效的算法解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2019牛客暑期多校训练营(第二场) H Second Large Rectangle 【次大全1子矩阵和】【单调栈】

题意

在01矩阵中找到第二大的全1矩阵,若没有第二大的矩阵则输出0。

题目链接:

https://ac.nowcoder.com/acm/contest/882/H

题解

这个题目题意看起来很简单,但是真的把我打自闭了,矩阵n,m大小在1~1000。如果暴力的话O(n3)还是会TLE的。
以下讲讲我的思路。搞清楚第二大矩阵,首先我们要解决的问题是:

  1. 怎么判断矩阵

  2. 怎么找到最大矩阵

  3. 怎么找到第二大矩阵

    一、判断矩阵的方法很简单,我们只要确保矩阵里面全是1就好了。
    二、找到最大矩阵有一点小技巧。
      举例来说:5*5的矩阵
      1 1 1 1 1
      1 1 1 1 0
      1 1 1 0 0
      1 1 0 0 0
      1 1 0 0 0
      我们定义一个数组h来记录这个矩阵的高,从上加到下,如果为0则定义为0,那么h矩阵就是:
      1 1 1 1 1
      2 2 2 2 0
      3 3 3 0 0
      4 4 0 0 0
      5 5 0 0 0
      以h矩阵的h[i][j]为矩阵的左下角,用数字d当作矩阵的宽度,从1开始递增(直到右边的数为比他小为止)矩阵的大小则为d*h。
      三、找到第二大的矩阵其实很简单
      用max1和max2来记录矩阵的最大值和一个比最大值小的数。然后再去维护这两个数,遇到比最大值大的就修改最大值,遇到在两个数之间的就修改第二个值。
    所以我们可以算出所有矩阵的大小,然后通过max1和max2来找到第二大的矩阵
    没错,虽然说我的思路很清晰,但是还是搞不定呀。。。
    询问过学长后才知道需要用【单调栈】来优化算法

单调栈

单调栈顾名思义就是栈里面的元素都是单调性的(单调增或单调减)
这里用单调栈储存数组的下标。j从1遍历到m,如果h[j]<h[栈顶]的话就计算该矩阵的面积,然后将栈顶元素弹出,再重复比较h[j]和h[栈顶]。最后把h[j]压入栈中。
由于使用单调栈,我们求得是最大子矩阵,所以要把包含在最大子矩阵得次大
子矩阵的情况算进去,所以对于取值的时候要特判 (d-1,h) 和 (d,h-1),d代表底,h代表高。
例如:2*3的矩阵
1 1 0 0
1 1 1 1
h矩阵为:
1 1 0 0
2 2 1 1
i = 1时:先将0压入栈中,再将1、2压入栈中,栈的元素为0 1 2 。
因为h[3] == 0 < 1,所以计算面积 max1= 1,max2 = 2。
i = 2时:先将0压入栈中,再将1、2压入栈中,栈的元素为0 1 2 。
因为h[3] == 1 < 2,所以计算面积 max1= 2,max2 = 4。
然后栈的元素再变为0 3 4,当4后面没有数了,直接计算。
最后算出面积是 max1= 4,max2 = 4。

AC代码

#include<string.h>
#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;
  
int matrix[1005][1005];
int h[1005];
int r[1005];
int l[1005];
int vec[2]={0,0};
void Maxmat(int x,int y)
{
    int ans = x*y;
    if(ans > vec[1])
    {
        vec[0] = vec[1];
        vec[1] = ans;
    }
    else if(ans>vec[0])
    {
    	vec[0] = ans;
	}
}
int main()
{
   int n,m;
   while(scanf("%d %d",&n,&m)!=EOF)
   {
    	int i,j;
      	int num =0;
      	for(i=1;i<=n;i++)
      	{
      	   for(j=1;j<=m;j++)
      	   {
            	scanf("%1d",&matrix[i][j]);
            	if(matrix[i][j]==1)
             	   num++;
         	}
      	}
      	memset(h,0,sizeof(h));
      	for(i=1;i<=n;i++)
      	{
      	   	for(j=1;j<=m;j++)
      	   	{
      	     	if(matrix[i][j]==1)
        	    	h[j]++;
        		else
               h[j]=0;
         	}
         	h[0]=h[m+1]=-1;
     		stack<int>st;
			st.push(0);
			h[0] = -2;//防止栈空 
			h[m + 1] = -1;
			for(int j = 1; j <= m+1; j++) 
			{
				while (h[st.top()] > h[j]) 
				{
					int index = st.top();
					st.pop();
					int d = j-1-st.top(); //当前位置往前  
					Maxmat(d, h[index]);//最大的情况 
					
					Maxmat(d-1, h[index]);//包含在最大内的次大的情况 
					Maxmat(d, h[index]-1);
				}
				st.push(j);
			}
      	}
        if(num>=2)
            printf("%d\n",vec[0]);
        else
            printf("0\n");
   }
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值