1. 程式人生 > 其它 >演算法-17求最大子矩陣的大小

演算法-17求最大子矩陣的大小

描述

給定一個整型矩陣 map,其中的值只有 0 和 1 兩種,求其中全是 1 的所有矩形區域中,最大的矩形區域裡 1 的數量。

輸入描述:

第一行輸入兩個整數 n 和 m,代表 n*m 的矩陣
接下來輸入一個 n*m 的矩陣

輸出描述:

輸出其中全是 1 的所有矩形區域中,最大的矩形區域裡 1 的數量。

示例1

輸入:
1 4
1 1 1 0
輸出:
3
說明:
最大的矩形區域有3個1,所以返回3

思路

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

依次以矩陣每一行j為底,往j-1結算每一列有多少個連續的1,將結果存在一個數組height[j]。例如以矩陣第三行[1,1,1,1,1]往1,2行結算,可得height[j]={3,1,3,2,2}。對於每次結算得到height[j]陣列,我們可以將其看成一個直方圖。如[3,1,3,2,2]可以看成如下直方圖:

 

然後我們依次求出可以這個height[j]陣列中最大矩形的大小maxArea是多少,依次更新這個maxArea,就可以得到結果。

如何求height[j]陣列的最大矩形大小
求陣列最大矩形大小,我們是利用一個單調棧來實現的。單調棧顧名思義就是棧的元素從棧底到棧頂依次是遞增或遞減的。利用單調遞增棧我們可以求出陣列中每一個元素左邊離它最近比它小的位置和右邊離它最近比它小的位置在哪裡。例如對於陣列{3,1,3,2,2}對於第一個元素左邊離它最近比它小的位置為空,右邊離它最近比它小的位置在哪裡為1。如何利用單調遞增棧實現上述過程呢?在將陣列元素壓入棧時需要遵循下面的規則:

如果當前棧stack為空或者當前陣列的元素height[j]>=height[stack.top()](棧頂元素),那麼直接把當前元素的位置j壓入stack。
如果當前棧不為空且當前陣列元素height[j]<=height[stack.top()](棧頂元素),那麼依次從stack彈出元素,並結算棧頂元素為根基,向左和向右分別可以拓展到哪裡,則可以得出當前最大子矩陣的大小為多少。
結算最大矩陣大小思想如下:

如何當前遍歷的元素的元素為i,當前棧頂元素元素為j,彈出棧頂元素後,新的棧頂元素為k。那麼現在考慮以元素為j為根基,其向左最左能達到k+1,因為j最左最近小於j的元素應該為k,那麼向右最遠應該能到i-1,因為j之所以被彈出,就是因為遇到了第一個比位置j值小的位置。所以其最大子矩陣大小結算為(i-k-1)*height[j]。

程式碼如下:
import java.util.Scanner;
import java.util.Stack;
 
public class Main{
 
    public static int findMaxFize(int[][] arr){
        
if(arr == null || arr.length == 0 || arr[0].length == 0) return 0; int maxArea = 0; int[] h = new int[arr[0].length]; for(int i = 0; i < arr.length; i++){ for(int j = 0; j < arr[0].length; j++){ h[j] = arr[i][j] == 0 ? 0 : h[j]+1; } maxArea = Math.max(maxArea, maxRecFromBottom(h)); } 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; } public static void main(String args[]){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int m = sc.nextInt(); int[][] arr = new int[n][m]; for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++) arr[i][j] = sc.nextInt(); } int res = findMaxFize(arr); System.out.println(res); } }