面积最大的全1子矩阵(腾讯2012年暑期实习生招聘面试二面试题)

 

题目1497:面积最大的全1子矩阵

时间限制:1 秒

内存限制:128 兆

特殊判题:

提交:1019

解决:215

题目描述:

在一个M * N的矩阵中,所有的元素只有0和1,从这个矩阵中找出一个面积最大的全1子矩阵,所谓最大是指元素1的个数最多。

输入:

输入可能包含多个测试样例。
对于每个测试案例,输入的第一行是两个整数m、n(1<=m、n<=1000):代表将要输入的矩阵的大小。
矩阵共有m行,每行有n个整数,分别是0或1,相邻两数之间严格用一个空格隔开。

输出:

对应每个测试案例,输出矩阵中面积最大的全1子矩阵的元素个数。

样例输入:
2 2
0 0
0 0
4 4
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
样例输出 0
4
来源:
腾讯2012年暑期实习生招聘面试二面试题

 

题目链接:http://ac.jobdu.com/problem.php?pid=1497

 

思路:单调栈的应用;O(n*m);

 

首先把原矩阵num[][],进行预处理:使得len[i][j],

表示第i行j列为底的最大连续宽度,(len[][]从0行0列开始) if(num[i][j]) len[i][j]=len[i][j-1]+num[i][j]; else len[i][j]=0;

举个例子:设矩阵num(1行,1列开始)为:

                                                                               0  -1  -1  -1  -1

    0 1 1 1                                                                0   0   1   2   3

    1 1 1 0              则相应的len[][]矩阵预处理为:  0   1   2   3   0

    1 0 1 1                                                                 0   1   0   1   2

    1 1 1 1                                                                 0   1   2   3   4

 

从最后一列开始遍历,每次从第一行入栈,当后面的数比前面大的时候就入栈,当不大于栈顶的数时,栈顶元素就出栈并计算面积;

处理技巧先入栈0(值一个负数值)保证不会发生段位错误;

举个简单的例子:假设某一列的值为 -1 2 3 5 3 4 2 4

                                    对应的行标为  0 1 2 3 4 5 6 7

首先0压入栈,然后压入1(因为2>-1),然后压入2(因为3>2),然后弹出3(因为3<=5)并计算高为5宽度为W=4-2-1;记录面积tmp=max(tmp,5*W);然后弹出2(因为3<=3)并计算高为3宽度为W=4-1-1;记录面积tmp=max(tmp,3*W);然后压入4;然后压入5;然后弹出5(.....)并计算面积,然后弹出3,并计算面积;然后弹出1并计算面积;然后压入6,最后压入7;最后没有数据了,对栈内元素进行处理面积,依次弹出,宽度为最后一个压入的元素;。。。依次对每列操作,

//小优化,ans表示结果,当ans>=j列*m行时就可以break了。就是这样,注意细节;

 

这题我以前也写过,现在来更新下,之前的代码可以参考:http://www.cnblogs.com/yuyixingkong/p/4156602.html

 

思维比较重要!

 

#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;
int num[1005][1005];
int len[1005][1005];
int main()
{
    int m,n;
    while(scanf("%d %d",&m,&n)!=EOF)
    {
        for(int i=1;i<=m;i++)
        {
            len[i][0]=0;
            for(int j=1;j<=n;j++)
            {
                len[0][j]=-1;
                scanf("%d",&num[i][j]);
                if(num[i][j]) len[i][j]=len[i][j-1]+num[i][j];
                else len[i][j]=0;
            }

        }
        stack<int> S;
        int ans=0,tmp;
        for(int j=n;j>0;j--)
        {
 //           S.clear();
            int W,L;
            tmp=0;
            if(ans>=j*m) break;
            S.push(0);
            for(int i=1;i<=m;i++)
            {
                if(len[i][j]>len[S.top()][j]) {S.push(i);}
                else
                {
                    while(!S.empty()&&len[i][j]<=len[S.top()][j])
                    {
                        L=S.top();
                        S.pop();
                        int p=S.top();
                        W=(i-p-1);
                        tmp=max(tmp,W*len[L][j]);

                    }
                    S.push(i);
                }
            }
            int LL;
            if(!S.empty())LL=S.top();
            while(!S.empty())
            {
                L=S.top();
                S.pop();
                int p=S.top();
                if(p) W=(LL-p);
                else {W=LL;S.pop();}
                tmp=max(tmp,W*len[L][j]);

            }
            ans=max(tmp,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}
/*
4 4
0 1 1 0
1 1 1 0
0 0 1 1
1 1 1 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值