单调栈-all-one_matrices

链接:https://ac.nowcoder.com/acm/contest/888/A
来源:牛客网

题目描述

Gromah and LZR entered the great tomb, the first thing they see is a matrix of size n×mn\times mn×m, and the elements in the matrix are all 0 or 1.

LZR finds a note board saying "An all-one matrix is defined as the matrix whose elements are all 1, you should determine the number of all-one submatrices of the given matrix that are not completely included by any other all-one submatrices".

Meanwhile, Gromah also finds a password lock, obviously the password should be the number mentioned in the note board!

Please help them determine the password and enter the next level.

输入描述:

The first line contains two positive integers n,m, denoting the size of given matrix.
 
Following n lines each contains a string with length m, whose elements are all or 1, denoting the given matrix.
 
 
1≤n,m≤3000

输出描述:

Print a non-negative integer, denoting the answer.
示例1

输入

3 4
0111
1110
0101

输出

5

说明

The 5 matrices are (1,2)−(1,4),  (1,2)−(2,3),  (2,1)−(2,3),  (3,1)−(3,4),(4,4)-(4,4).

不会单调栈的我当场自闭。。。出来学了一下感觉也没有那么难。单调栈如名字所表示的就是一个具有单调性质的栈,遇到入栈的时候就弹出栈顶直到入栈后维持单调。一个基本的例题是求最大全1矩阵,当弹出栈顶的时候就说明一个矩形结束,此时计算这个矩形的大小。

本题就是枚举每行,对每行求全1矩阵的数量。但这里有两个问题,一是会重复,二是不能保证所求都是最大的。所以我们要检查当前得到的矩阵能不能和上一行的矩阵合并,如果能的话就不加入。这里有几种做法,我采用的是维护一个和单调栈同步操作的栈记录对应矩形的左端点和一个记录对应左右端点出现行位置的表,每次弹出的时候得到矩形的左右端点,在表里看对应区间在上一行有没有出现过。

代码如下
#include<bits/stdc++.h>
using namespace std;
const int MAX=3010;
int mps[MAX][MAX];
int sum[MAX][MAX];
int left[MAX]; 
int mp[MAX][MAX];
char str[MAX];
int main(){
    int n,m,ans(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        scanf("%s",str+1);
        for(int j=1;j<=m;j++)
            mps[i][j]=str[j]-'0';
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        sum[i][j]=mps[i][j]?sum[i-1][j]+1:0;
    
    memset(mp,-1,sizeof(mp));
    for(int i=1;i<=n;i++){
        stack<int>high,left;//除高度外维护一个同进同出的左边界栈 
        for(int j=1;j<=m+1;j++){
            int lf=j;
            while(!high.empty()&&sum[i][j]<high.top()){
                if(mp[left.top()][j]!=i-1) ans++;//左右边界是left.top(),j的层是不是上一层 
                mp[left.top()][j]=i;
                lf=left.top();
                left.pop();
                high.pop();
            }
            
            if((high.empty()&&sum[i][j])||(!high.empty()&&sum[i][j]>high.top())){
                high.push(sum[i][j]);
                left.push(lf);
            }
        }
        
        
    }
    cout<<ans<<endl;
    
} 
View Code

 

 
posted @ 2019-08-12 10:07  wengsy150943  阅读(150)  评论(0编辑  收藏  举报