【題目】
給定一個整型矩陣map,其中的值只有0和1兩種,求其中全是1的所有矩形區域中,最大的矩形區域爲1的數量。
例如:
1 1 1 0
其中,最大的矩形區域有3個1,所以返回3
再如:
1 0 1 1
1 1 1 1
1 1 1 0
其中,最大的矩形區域有6個1,所以返回6
【解答】
1、矩陣的行數爲N,以每一行做切割,統計以當前行作爲底的情況下,每個位置往上爲1的數量。使用高度數組height來表示
例如
1 0 1 1
1 1 1 1
1 1 1 0
以第1行做切割後,height={1,0,1,1},height[j]表示目前的底上(第1行),j位置往上(包括j位置)有多少個連續的1。
以第2行做切割後height={2,1,2,2},注意到從第一行到第二行,height數組的更新是十分方便的,即height[j] = map[i][j]+1。
以第3行做切割後,height={3,2,3,0}
2、對於每一次切割 ,都利用更新後的height數組來求出以每一行爲第的情況下,最大的矩形是什麼。那麼這麼多次切割後,最大的矩形就是我們要的。講一下怎麼求最大的矩形,利用棧,比如,height是[3,2,3,0],就讓他們依次入棧,要求從棧頂到棧底要始終滿足從大到小,如果將要入棧的大於棧頂元素,則直接將其壓入,如果將要入棧的小於棧頂元素,那就將棧頂元素依次彈出,直到滿足入棧的元素大於棧頂元素才讓其入棧,那麼,注意下面這句話,當出現要入棧的元素小於棧頂元素的時候,將要入棧的元素的位置記爲i,棧頂元素爲j,棧頂元素下面的元素爲k,那麼,當前形成的最大的矩形就是(i-k-1)*height[j],即使在中間的時候有時候會計算錯誤(具體錯誤自己琢磨吧),在最後也會被糾正過來
整個過程就是如下代碼中的maxRecSize方法。步驟2的實現是如下代碼中的maxRecFromBottom方法,具體的細節自己走代碼吧
import java.util.*;
class test{
public static void main(String[] args){
//定義map矩陣
int[][] map = {{1,0,1,1},{1,1,1,1},{1,1,1,0}};
//調用函數
int max = maxRecSize(map);
//輸出
System.out.println(max);
}
public static int maxRecSize(int[][] map){
//如果是空的,返回
if(map==null || map.length==0 || map[0].length==0){
return 0;
}
//用來存放最大值,最後返回
int maxArea = 0;
//高度數組
int[] height = new int[map[0].length];
//計算高度數組
for(int i=0;i<map.length;i++){
for(int j=0;j<map[0].length;j++){
height[j] = map[i][j]==0?0:height[j]+1;
}
//在每一層都計算一遍最大子矩陣,找到最大的
maxArea = Math.max(maxRecFromBottom(height),maxArea);
}
return maxArea;
}
public static int maxRecFromBottom(int[] height){
//如果高度數組爲空,返回
if(height==null || height.length==0){
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<Integer>();
for(int i=0;i<height.length;i++){
while(!stack.isEmpty() && height[i]<=height[stack.peek()]){
int j = stack.pop();
int k = stack.isEmpty()?-1:stack.peek();
int curArea = (i-k-1)*height[j];
maxArea = Math.max(maxArea,curArea);
}
stack.push(i);
}
while(!stack.isEmpty()){
int j = stack.pop();
int k = stack.isEmpty()?-1:stack.peek();
int curArea = (height.length-k-1)*height[j];
maxArea = Math.max(maxArea,curArea);
}
return maxArea;
}
}