求最大子矩阵的大小

【题目】给定一个整型矩阵matrix,其中的值只有0和1两种,求其中全是1的所有矩形区域,最大的矩形区域为1的数量。

采用贪心算法。将问题分解成子问题。
子问题定义:现在可以求的以某一行作为矩形最后一行时 矩形的最大区域
原问题的解就是从0到m-1行获得的最大矩形的最大矩形,也就是m个子问题解的最优解。
问题变为求解第i行结尾的矩形的最大值是多少。
给定矩阵matrix:
第i行 以第i行结尾的矩形最大是多少?先根据第i-1行得出第i行上列上值的累加record[]数组。当matrix[i][j]==0时,record[j] = 0。当matrix[i][j]==1时,更新record[j] = record[j] +1;
得出record后,相当于给定一个数组,数组能画出一个直方图,求以数组中一个数向左向右扩成的矩形面积最大值。就是子问题的解。
原来的代码是这么写的,但是只能过50%。

public class maximal_rectangle {
    public static void main(String[] args) {
        //char[][] matrix = {{'1','1','0','1'},{'1','1','0','1'},{'1','1','0','1'}};
        //char[][] matrix = {{'1','0','1','1'}};
        //char[][] matrix = {{'1'},{'0'},{'1'}};
        char[][] matrix = {{'0','0','0','1','0','1','0'},
                {'0','1','0','0','0','0','0'},
                {'0','1','0','1','0','0','1'},
                {'0','0','1','1','0','0','1'},
                {'1','1','1','1','1','1','0'},
                {'1','0','0','1','0','1','1'},
                {'0','1','0','0','1','0','1'},
                {'1','1','0','1','1','1','0'},
                {'1','0','1','0','1','0','1'},
                {'1','1','1','0','0','0','0'}};

        System.out.println(maximalRectangle(matrix));
    }

    public static int maximalRectangle(char[][] matrix) {
        if (matrix == null || matrix.length == 0) {
            return 0;
        }
        int max = Integer.MIN_VALUE;
        int m = matrix.length, n = matrix[0].length;
        int[] record = new int[n];
        int[] dp1 = new int[n], dp2 = new int[n]; //dp1[i],以record[i]为结尾表示记录从左往右方向,下降的子串,dp2[i],以record[i]为开头,从右向左方向下降的子串个数
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == '1') {
                    record[j] += 1;
                } else {
                    record[j] = 0;
                }
            }
            dp1[0] = 1;
            for (int j = 1; j < n; j++) {
                if (record[j] <= record[j-1]) {
                    dp1[j] = dp1[j-1] +1;
                } else {
                    dp1[j] = 1;
                }
            }

            dp2[n-1] = 1;
            for (int j = n-2; j >= 0; j--) {
                if (record[j] <= record[j + 1] ) {
                    dp2[j] = dp2[j +1] + 1;
                } else {
                    dp2[j] = 1;
                }
            }

            for (int j = 0; j < n; j++) {
                int sum = (dp1[j] + dp2[j] -1) * record[j];
                max = max < sum ? sum : max;
            }
        }
        return max;
    }

}

测试用例中正确的解是6,我上面的代码运行的结果是4。

char[][] matrix = {{'0','0','0','1','0','1','0'},
                {'0','1','0','0','0','0','0'},
                {'0','1','0','1','0','0','1'},
                {'0','0','1','1','0','0','1'},
                {'1','1','1','1','1','1','0'},
                {'1','0','0','1','0','1','1'},
                {'0','1','0','0','1','0','1'},
                {'1','1','0','1','1','1','0'},
                {'1','0','1','0','1','0','1'},
                {'1','1','1','0','0','0','0'}};

代码逻辑错误在于 对record求最长下降子串

先根据第i-1行得出第i行上列上值的累加record[]数组。当matrix[i][j]==0时,record[j] = 0。当matrix[i][j]==1时,更新record[j] = record[j] +1;

这个逻辑不能判断record[j] 和 record[j- record[j-1]] 谁大谁小。因为record[j-1]仅仅能知道比record[j-1]大的值有几个,若record[j]< record[j-1] 这些值确实比record[j]大,但是还会漏掉解。
比如 record[] = {1,3,8,7,2}
对应的dp1[] ={1, 1, 1, 2, 3} 难道比2对应下标的dp1的值 是 1 + dp1[3] 吗,显然漏掉了3这个数。

要想实现O(N)方法求数组直方图能形成的最大矩形,还是得用栈。
思路:
1、record[j] > record[j-1],下标直接入栈。
2、record[j] <= record[j-1],
若record[j] < record[j-1] 弹出栈顶元素,此元素的右边值小于自身,不能往右边扩,左边的下标和当前j之间间隔的距离就是横跨了几个直方图。
record遍历完成后,再考虑stack出栈

import java.util.Stack;
public class Solution {
    public int maximalRectangle(char[][] matrix) {
        if (matrix == null || matrix.length == 0) {
            return 0;
        }
        int max = Integer.MIN_VALUE;
        int m = matrix.length, n = matrix[0].length;
        int[] record = new int[n];
        int[] dp1 = new int[n], dp2 = new int[n]; //dp1[i],以record[i]为结尾表示记录从左往右方向,下降的子串,dp2[i],以record[i]为开头,从右向左方向下降的子串个数
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == '1') {
                    record[j] += 1;
                } else {
                    record[j] = 0;
                }
            }
            int area = maxRectFromBottom(record);
            max = max < area ? area : max;
        }
        return max;
    }

    public int maxRectFromBottom(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }
        int maxArea = 0;
        int area;
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < height.length; i++) {
            while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
                int index = stack.pop();
                if (stack.size() == 0) {
                    area = i * height[index];
                } else {
                    area = (i - stack.peek() - 1) * height[index];
                }
                maxArea = maxArea < area ? area : maxArea;
            }
            stack.push(i);
        }
        while (!stack.empty()) {
            int index = stack.pop();
            if (stack.size() == 0) {
                area = height[index] * height.length;
            } else {
                area = height[index] * (height.length - 1 - stack.peek());
            }
            maxArea = maxArea < area ? area : maxArea;
        }
        return maxArea;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_31617121/article/details/80157533