全是1的最大子矩阵 (51Nod 1158)------Java从0开始学习系列之路(9)

前言----

人在,博客在! :)

全是1的最大子矩阵

题目:

给出1个M*N的矩阵M1,里面的元素只有0或1,找出M1的一个子矩阵M2,M2中的元素只有1,并且M2的面积是最大的。输出M2的面积。

Input

第1行:2个数m,n中间用空格分隔(2 <= m,n <= 500)
第2 - N + 1行:每行m个数,中间用空格分隔,均为0或1。

Output

输出最大全是1的子矩阵的面积。

Input示例

3 3
1 1 0
1 1 1
0 1 1

Output示例

4

题目分析:

这道题啊,很有趣啊。!!!直接开门见山,大家直接跟着我的思路走:(以例子中的矩形为例

如果我能做到max(第一行为底的最大全为1矩阵,第二行为底的最大全为1的矩阵,第三行为底的最大全为1的矩阵)是不是就是解决了问题呢??

那么问题就转化成了,如何求每一行作为底时的最大全为1的矩阵呢?

没错,这就是本题的最关键之处,没了解之前很难想到如何解决,但是了解了之后就忘不了了。我们用height[j]表示以当前行为底时,第j列的柱子高度(即从该行开始往上数,该列有多少个连续的1)。

当在第一行(从上往下数)时:height={1,1,0};

当在第二行时:height={2,2,1}

当在第三行时:height={0,3,2};

接下来我们会发现 height[j](第i行) =  map[i][j] == 1? height[j](第i-1行)+1:0 ;

恩恩,那么这时我给大家个图,大家就知道预处理成柱子的作用是啥了,很妙的,

此时如果我 们求出该直方图中的最大矩形面积,就是求出了以该行为底的最大矩形面积了,是不是很妙。说白了,就是以该行为底的最大矩形面积 = max(各个柱子的高作为宽时所能拓展的最大矩形}.但是如何快速的求每个柱子拓展的最大矩形面积呢,此时我们只要找到每个柱子左边和右边第一个比它低的柱子的长度,就可以很轻松计算出来以该柱子的高度作为宽时所能构成的最大矩形。是不是又兴奋了,又是要用单调栈的节奏!,这边是用单调递增栈,精髓是:一个数入栈是因为遇到左边第一个比它小的数或者左边没有比它小的数了;一个数出栈时因为遇到右边第一个比它大的数。

 

附AC代码(JAVA,,书上的代码貌似比我自己要好,但是我感觉我的虽然搓一点,但是便于理解一点,接下来考读者自己去啃啦)

package code_180;

import java.util.*;
public class MaxSubArray {
	
	int n ,m ,ans;
	private int left[]; //用来处理每根柱子左边第一根比它小的柱子的位置
	private int right[];//用来处理每根柱子右边第一根比它小的柱子的位置
	private int height[]; //用来存储柱子的高度
	LinkedList<Integer>stack;
	
	MaxSubArray(int n, int m){
		
	   this.n= n;
	   this.m = m;
	   stack = new LinkedList<Integer>();
	   left = new int[m + 2];
	   right = new int [m+2];
	   height = new int[ m+2 ];
	   
	}
	
	public int getMax(int [][]array) {
		
		int max =-1;
		for(int i =  0 ;i < n ;i++) {
			
            //求第i行为底的各个柱子高度
			for(int j = 0 ;j < m ;j++) {
				
				height[j] = (array[i][j] == 1? height[j]+1 : 0);
			}
			
   

			int temp = getCurMax(i); //求以当前行为底的直方图中的最大矩形面积

			max = temp> max? temp: max;		
			
		}
		
	

		return max;
	}
	
	public int getCurMax(int i) {  //用来得到以当前行为底的最大矩形高度
		
		
		int max = -1;
		for(int j = 0 ;j < m ;j++) {
			
			while(!stack.isEmpty()&& height[stack.peek()] >= height[j]) {
				
				right[stack.pop()] = j;
			}
			
			if(!stack.isEmpty()) {
				left[j] = stack.peek();
			}else {
				
				left[j] = -1;
			}
			
			stack.push(j);
		}
			
			while(!stack.isEmpty()) {
				
				right[stack.pop()] = -1;
			}
			
			
			for(int j = 0 ;j < m ;j++) {
				
				
				if(left[j]!=-1 && right[j]!=-1) 
					max = height[j]*(right[j]  - left[j] - 1 ) > max? height[j]*(right[j] - left[j] - 1) :max  ;
	
				else if(left[j] == -1 && right[j] ==-1) 
					max = height[j]*m  > max? height[j]*m:max;
		
				else if(right[j] ==-1) 
					max =  height[j]*(m  - left[j] - 1) >max? height[j]*(m - left[j] -1):max ;
			
				else if(left[j] == -1) 
				   max  =  height[j]*right[j] >max? height[j]*right[j] :max;
				   

			}
			
			return max;	
		 
  }
	
	public static void main(String []args) {
		
		Scanner cin = new Scanner(System.in);
		int n,m;
		n = cin.nextInt();
		m = cin.nextInt();
		int array[][] = new int[n + 2][m +2];

		for(int i = 0 ; i < n ;i++) {
			for(int j = 0 ;j < m ;j++) {
			 
				array[i][j] = cin.nextInt();
				
			}
			
		}
		
		MaxSubArray mySubArray = new MaxSubArray(n,m);
		System.out.println( mySubArray.getMax(array) );
		
		
		
		
	}

}

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值