1-9 求最大子矩阵问题

题目描述

  • 求最大子矩阵问题。
    在这里插入图片描述

解题方法1

  • 首先,我们以每一行做切割统计以当前行作为底的情况下,每个位置往上1的数量(遇到0为止),这样针对每一行我们都可以得到一个数组。如:第一行数组为 1 0 1 1,第二行数组为 2 1 2 2,第三行数组为 3 2 3 0。
  • 而针对每一行得到的那个数组,我们可以看做一个条形统计图,然后在这个条形统计图中找到的最大矩形的面积就是每一行对应的最大子矩阵中元素数量。最后依次进行比较就可以得到全局的最大子矩阵。
  • 如下为 3 2 3 0对应的条形统计图
    在这里插入图片描述
  • 因此原题可以拆分为两个子问题。1 求出以每一行作为底得到的那个表示条形统计图的数组,2 根据条形统计图求出对应最大子矩阵的面积。 最后将两个问题的结果整合一下得到全局最大子矩阵。
  • 对于问题一比较简单,遍历每一行,对于第一行的目标数组就是第一行的各元素,对于第二行的目标数组,如果当前元素为0说明目标数组对应值为0,如果当前元素为1说明目标数组对应值为上一行目标数组对应值加一。
  • 问题一代码如下
//根据一个矩阵,打印每一行对应的表示条形统计图的数组
public static int getmax(int [][] arr){
    int[] pre = new int[arr[0].length]; //保存上一行的目标数组
    for(int i=0;i<arr.length;i++){
        int[] line = arr[i]; //当前行
        if(i==0){
            for(int j=0;j<line.length;j++){
                pre[j] = line[j];
            }
        }
        else{
            for(int j=0;j<line.length;j++){
                if(line[j]==0){
                    pre[j] = 0;
                }
                else{
                    pre[j] = pre[j]+1;
                }
            }
        }
        //打印当前行的目标数组
        for(int a:pre){
            System.out.print(a+" ");
        }
        System.out.println();
    }
    return 0;
}
  • 对于问题二,如何才能快速高效计算出条形统计图的最大矩形,起初的设想是用最短的一列乘以总列数,但是这是不对的,比如:2 3 2 1的最大矩形面积是6而不是4。
  • 我们可以扫描每一列,对于每一列进行左右扫描直到遇到更短的列扫描结束,然后用列长乘以扫描列数就是每一列对应的矩形面积。比如2 3 2 1,第一列扫描面积为23=6,第二列31=3,第三列23=6,第四列14=4。
  • 这个行为的本质就是寻找每一列左边第一个比当前列小的位置在哪里以及右边第一个比当前列小的位置在哪里,具体应该如何扫描才能使得时间复杂度最小呢?可以使用栈。
  • 以 3 4 5 4 3 6为例,我们使用栈来存放元素的下标,保证栈从栈底到栈顶是递增的。遇到值比当前栈顶大的直接入栈,否则将栈顶出栈直到当前值大于栈顶再入栈。
  • 出入栈的规则如下:
    3的下标0入栈,4的下标1入栈,5的下标2入栈,5的下标2出栈,4的下标2出栈,新的4的下标3入栈…
  • 明确出栈和入栈规则后我们就可以针对每一列计算它左右的扩展边界,计算每一列的扫描面积。对于每一列的下标,要等到它出栈的那一刻才能明确它的左右边界。
    在这里插入图片描述
  • 所以对于位置j的柱子,它的最大扫描面积为 (i-1-(k+1)+1)乘上当前柱子的高度。
  • 下面编写问题二的代码,需要注意的是当j出栈后栈为空时说明目前为止j的元素最小那么j的左边界为0,如果j的元素值与i的元素值相等,就按i-1为右边界计算不影响最终结果。
//根据一个表示条形统计图的数组,计算它的最大扫描的面积。
public static int getmaxline(int [] height){
    Stack<Integer> s = new Stack<>();
    int maxscan = 0;
    for(int i=0;i<height.length;i++){
        if(s.empty() || height[i]>height[s.peek()]){
            s.push(i);
        }
        else{
            while(!s.empty() && height[i]<=height[s.peek()]){
                int j =s.pop();
                int k = s.empty()?-1:s.peek();
                int left = k+1;
                int right = i-1;
                int scan = (right-left+1)*height[j];
                if(scan>maxscan){
                    maxscan = scan;
                }
            }
            s.push(i);
        }
    }
    while(!s.empty()){
        int j =s.pop();
        int k = s.empty()?-1:s.peek();
        int left = k+1;
        int right = height.length-1;
        int scan = (right-left+1)*height[j];
        if(scan>maxscan){
            maxscan = scan;
        }
    }
    return maxscan;
}
  • 最后将两个步骤整合,完成最终代码
public class Test {
    public static void main(String[] args) {
        int[][] arr = {
                       {1,0,1,1},
                       {1,1,1,1},
                       {1,1,1,0}
                      };
        int max = getmaxmap(arr);
        System.out.println(max);
    }
    //根据一个表示条形统计图的数组,计算它的最大扫描的面积。
    public static int getmaxline(int [] height){
        Stack<Integer> s = new Stack<>();
        int maxscan = 0;
        for(int i=0;i<height.length;i++){
            if(s.empty() || height[i]>height[s.peek()]){
                s.push(i);
            }
            else{
                while(!s.empty() && height[i]<=height[s.peek()]){
                    int j =s.pop();
                    int k = s.empty()?-1:s.peek();
                    int left = k+1;
                    int right = i-1;
                    int scan = (right-left+1)*height[j];
                    if(scan>maxscan){
                        maxscan = scan;
                    }
                }
                s.push(i);
            }
        }
        while(!s.empty()){
            int j =s.pop();
            int k = s.empty()?-1:s.peek();
            int left = k+1;
            int right = height.length-1;
            int scan = (right-left+1)*height[j];
            if(scan>maxscan){
                maxscan = scan;
            }
        }
        return maxscan;
    }
    //根据一个矩阵,打印每一行对应的表示条形统计图的数组
    public static int getmaxmap(int [][] arr){
        int[] pre = new int[arr[0].length]; //保存上一行的目标数组
        int max = 0; //保存最大面积
        for(int i=0;i<arr.length;i++){
            int[] line = arr[i]; //当前行
            if(i==0){
                for(int j=0;j<line.length;j++){
                    pre[j] = line[j];
                }
            }
            else{
                for(int j=0;j<line.length;j++){
                    if(line[j]==0){
                        pre[j] = 0;
                    }
                    else{
                        pre[j] = pre[j]+1;
                    }
                }
            }
            int s = getmaxline(pre);
            max = max<s?s:max;
        }
        return max;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值