zoukankan      html  css  js  c++  java
  • 每日一题 为了工作 2020 0311 第九题

    * 单调栈的相关应用

    /**
    * 问题:求最大子矩阵的大小
    * 给定一个整形矩阵map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
    * 例如:
    * 1 1 1 0
    * 其中,最大的矩形区域有三个1,所以返回3.
    * 再如:
    * 1 0 1 1
    * 1 1 1 1
    * 1 1 1 0
    * 其中最大的矩形区域有6个1,所以返回6.
    * 分析:
    * 如果矩阵的大小为O(N*M),可以做到时间复杂度为O(N*M)。解法具体过程如下所示:
    *
    * 1.矩阵的行数为N,以每一行做切割,统计以当前行作为底的情况下,每个位置往上1的数量。使用高度数组height来表示。
    *
    * 例如:
    * map = 1 0 1 1
    * 1 1 1 1
    * 1 1 1 0
    *
    * 1.以第一行做切割后,height={1,0,1,1},height[j]表示在目前的底(第一行)的j位置往上(包括j位置),有多少个连续的1。
    *
    * 2.以第二行做切割后,height={2,1,2,2},height[j]表示在目前的底(第二行)的j位置往上(包括j位置),有多少个连续的1。
    * 注意到从第一行到第二行,height数组的更新是十分方便的,即为height[j]=(map[i][j]==0?0:height[j]+1)。
    *
    * 3.以第三行做切割后,height={3,2,3,0},height[j]表示在目前的底(第一行)的j位置往上(包括j位置),有多少个连续的1。
    *
    * 2.对于每一次切割,都利用更新后的height数组来求出以当前行为底的情况下,最大的矩形是什么。那么这么多次切割中,最大的那个矩形就
    * 是我们要的答案。
    *
    * 整个 过程中就是如下代码中的maxRecSize方法。步骤2的实现是如下的maxRecFromBottom方法。
    *
    * 下面重点介绍一下步骤2是如何实现的,这也是这道题的最为重要的部分,如果height数组的长度为M,那么求解步骤2的过程可以做到时间
    * 复杂度为O(M)。
    *
    * 对于height数组,我们可以理解为一个直方图,比如{3,2,3,0},相当于横轴为1、纵轴为height[j]高度的矩形拼成的图形。所以我们
    * 可以认为步骤2的实质就是在一个大的直方图中求出最大矩形的面积。如果我们能够求出以每一根柱子扩展出去的最大矩形,那么其中的最大的矩
    * 形就是我们想找的。
    *
    * 例入:
    * 1.以第一根高度为3的柱子向左边无法扩展,它的右边是2,比3小,所以向右也无法扩展,则以第一根柱子为高度的矩形面积就是3*1=3。
    * 2.以第二根高度为2的柱子向左边可以扩展一个距离,因为它的左边是3,比2大。同理,右边也可以扩展一个柱子,所以以第二根柱子为
    * 高度的矩形面积就是2*3=6。
    * 3.以第三根高度为3的柱子向左边无法扩展,它的左边是2,比3小,同时向右也无法扩展,则以第三根柱子为高度的矩形面积就是3*1=3。
    * 4.以第四根高度为0的柱子向左边无法扩展,同时向右也无法扩展,则以第四根柱子为高度的矩形面积就是0*1=0。
    *
    * 所以:
    * 在当前的height数组为{3,2,3,0}拼成的直方图内,最大的矩形面积为6.
    *
    * 考察每一根柱子最大能够扩多大,就是找到柱子左边离他最近且最小的柱子位置在哪里,以及柱子右边离他最近且最小的位置在哪里,这就需要用
    * 到单调栈结构。
    *
    * 编程思路:
    *
    * 1.假设当前弹出的位置记为j,弹出j位置之后的新栈顶位置记为k,只需要找到j位置对应的柱子向左和向右最远可以扩多远。对于位置j来说,如果
    * height[j]>height[i]。i为当前遍历的位置,j之所以被弹出是因为遇到了第一个比自己小的柱子,所以向右最远可扩展到i-1位置。
    * 2.如果height[i]=height[j],那么i-1位置就不是向右扩展到最远的位置了,最起码j位置可以扩展到i位置,所以这样计算出来的矩形面积
    * 会变小,但是在这种情况下i位置向左也必然可以扩展到j位置,也就是j位置扩出来的最大矩形和i位置扩出来的最大矩形是同一个,j位置的虽然
    * 无法准确计算,但是主需要等i位置弹出计算即可。
    * 3.对于j位置对应的柱子,向左最远可以扩展到当前新栈顶的位置k+1.因为k位置得值就是栈内比j位置小对应元素小且离j最近的位置。如果栈空则
    * 记为-1,所以最大矩形的面积就是S=(i-k-1)*height[j]。
    * 4.清算阶段则认为i=height.length==6,依次弹出计算即可。
    *
    * @author 雪瞳
    *
    */

    import java.util.Stack;
    
    public class getMaxRecSize {
        public 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(maxArea, maxRecFromBottom(height));
            }
        return maxArea;
        }
        
        
        public int maxRecFromBottom(int height[]) {
            
            if(height ==null || height.length==0) {
                return 0;
            }else {
                int maxArea = 0;//最大面积
                Stack<Integer> stack = new Stack<>();
                int indexElement = 0;//当前位置的高度
                int popIndex = 0;//当前弹出位置元素
                int leftIndex = 0;//当前位置向左最远可以走到哪里
                int rightIndex = 0;//当前位置向右最远可以走到
                int curArea = 0;//当前弹出位置扩出区域的面积值
                for(int i=0;i<height.length;i++) {
                    indexElement = height[i];
                    while(!stack.isEmpty() && height[stack.peek()] >= indexElement) {
                        popIndex = stack.pop();
                        leftIndex = stack.isEmpty()?-1:stack.peek();
                        rightIndex = i;
                        curArea = (rightIndex-1-leftIndex)*height[popIndex];
                        maxArea = Math.max(maxArea, curArea);
                    }
                    stack.push(i);
    
                }
                //清算阶段
                while(!stack.isEmpty()) {
                    popIndex = stack.pop();
                    leftIndex = stack.isEmpty()?-1:stack.peek();
                    rightIndex = height.length;
                    curArea = (rightIndex-1-leftIndex)*height[popIndex];
                    maxArea = Math.max(maxArea, curArea);
                }
                return maxArea;
            }    
        }
            
    }
    import java.util.Random;
    import java.util.Scanner;
    
    public class testGetMaxRecSize {
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            Random rand = new Random();
            getMaxRecSize getMax = new getMaxRecSize();
            testGetMaxRecSize test = new testGetMaxRecSize();
            System.out.println("请输入行数和列数,N代表行,M代表列!");
            int N =sc.nextInt();
            int M = sc.nextInt();
            int array[][] = new int[N][M];
            int maxArea = 0;
            for(int i = 0;i<array.length;i++) {
                for(int j=0;j<array[i].length;j++) {
                    array[i][j]=(rand.nextInt(2));
                }
            }
            /*int maxArea= 0;
            int array[][]=new int[][] {
                {1,0,1,1},
                {1,1,1,1},
                {1,1,1,0}
            };*/
            test.show(array);
            maxArea = getMax.maxRecSize(array);
            System.out.println("最大的矩形面积为:"+maxArea);
        }
        public void show(int array[][]) {
            for(int arr[]: array) {
                for(int element: arr) {
                    System.out.print(element+"	");
                }
                System.out.println();
            }
            System.out.println();
        }
    }
  • 相关阅读:
    BZOJ 1452 Count(二维树状数组)
    BZOJ 1407 Savage(拓展欧几里得)
    BZOJ 1415 聪聪和可可(期望DP)
    BZOJ 1406 密码箱(数论)
    最大流小结
    UVA6531Go up the ultras
    二分图小结
    Codeforces Round #243 (Div. 1)
    图论模板集合
    zoj3416 Balanced Number
  • 原文地址:https://www.cnblogs.com/walxt/p/12462011.html
Copyright © 2011-2022 走看看