算法-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);
    }
}
复制代码

 

posted @   思凡念真  阅读(199)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示