2.15 求二维数组的子矩阵的最大和

来源:互联网 发布:linux系统安装方法 编辑:程序博客网 时间:2024/05/17 22:11

1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述

这里写图片描述

3. 问题分析

对于这种求优问题, 最基本的思路, 无疑便是穷举了
所以 我们这里三种思路, 均为穷举。。。
解法一 : 穷举左上角的点, 然后在穷举所有可能该宽高, 计算矩阵的和, 统计最大值
解法二 : 穷举左上角的点, 然后在穷举所有可能该宽高, 计算矩阵的和, 通过预处理一些数据, 是的这个计算时间复杂度降到O(1)
解法三 : 穷举上下边界, 然后在讲上下边界之间的数字矩阵组合转化为一个一维数组[将每一列的数字之和加起来作为该一位数组的元素], 然后在利用一位数组的解法来解决

编程之美原图 :
这里写图片描述

4. 代码

/** * file name : Test22FindMaxSumMatrix.java * created at : 2:21:57 PM May 24, 2015 * created by 970655147 */package com.hx.test03;public class Test22FindMaxSumMatrix {    // 找到二维数组  子矩阵最大的和    public static void main(String []args) {        int[][] intArr = {            {1, -1, 1, -1 },            {0, 0, 1, -1 },            {1, 3, 2, 1 },            {1, -2, -1, 0 }        };        findMaxSumMatrix01(intArr);        findMaxSumMatrix02(intArr);        findMaxSumMatrix03(intArr);    }    // 穷举    public static void findMaxSumMatrix01(int[][] arr) {        int max = Integer.MIN_VALUE;        for(int row=0; row<arr.length; row++) {            for(int col=0; col<arr[0].length; col++) {                int maxWidth = arr[0].length - col;                int maxHeight = arr.length - row;                for(int height=1; height<=maxHeight; height++) {                    for(int width=1; width<=maxWidth; width++) {                        int sum = sum(arr, row, col, width, height);                        if(sum > max) {                            max = sum;                        }                    }                }            }        }        Log.log(max);    }    // 先计算([0][0]) 到([row][col])的矩阵的和存入一个sum矩阵中    // 然后再在 穷举  [计算矩阵中的数据和的方式为 : sum[row+height][col+width] - sum[row+height][col] - sum[row][col+width] + sum[row+height][col] ]    private static void findMaxSumMatrix02(int[][] intArr) {        int[][] sum = get00ToSpecifiedGridSum(intArr);        int max = Integer.MIN_VALUE;        for(int row=0; row<intArr.length; row++) {            for(int col=0; col<intArr[0].length; col++) {                int maxWidth = intArr[0].length - col;                int maxHeight = intArr.length - row;                for(int height=1; height<=maxHeight; height++) {                    for(int width=1; width<=maxWidth; width++) {                        boolean isEnter = false;                        int tmp = -1;                        // 先初始化为[row+height-1][col+width-1]                         // 如果row>0  减去[row+height-1][col]                        // 如果col>0  减去[row][col+width-1]   如果进入了row>0的判断  说明这里多减了一个[row+height-1][col+width-1]  加回来                        tmp = sum[row+height-1][col+width-1];                        if(row > 0) {                            tmp-= sum[row+height-2][col];                            isEnter = true;                        }                         if(col > 0) {                            tmp -= sum[row][col+width-2];                            if(isEnter) {                                tmp += sum[row+height-2][col+width-2];                            }                        }                        if(tmp > max) {                            max = tmp;                        }                    }                }            }        }        Log.log(max);    }    // 枚举上下边界 然后利用一维数组的算法 求解    private static void findMaxSumMatrix03(int[][] intArr) {        int max = Integer.MIN_VALUE;        for(int up=0; up<intArr.length; up++) {            for(int down=up; down<intArr.length; down++) {                int[] colArr = getColArrayByRowColWidthHeight(intArr, up, down);                int tmp = findMaxSumSeq04(colArr);                if(tmp > max) {                    max = tmp;                }            }        }        Log.log(max);    }    // 从后向前遍历[从前向后也可以]    // 初始令start, all为最后一个元素    // 然后向前遍历  取start为intArr[i]和intArr[i]+start的较大者[如果start小于0, 则取intArr[i] ]        // 取去all 为当前all, 和当前start子数组之和的最大值    // 如果上一次循环的(start)小于0, 则废弃intArr[lastI], 以及其之前的部分    public static int findMaxSumSeq04(int[] intArr) {        int start = intArr[intArr.length-1], all = start;        for(int i=intArr.length-2; i>=0; i--) {            start = getMax(intArr[i], intArr[i] + start);            all = getMax(all, start);        }        return all;    }    // 获取x和y之间的较大者    public static int getMax(int x, int y) {        return x>y ? x : y;    }    // 将row, col处,  width, height的矩阵转换为一维矩阵, 将同一列视为一个单元    private static int[] getColArrayByRowColWidthHeight(int[][] intArr, int row01, int row02) {        int[] res = new int[intArr[0].length];        int idx = 0;        for(int col=0; col<res.length; col++) {            int sum = 0;            for(int row=row01; row<=row02; row++ ) {                sum += intArr[row][col];            }            res[idx ++] = sum;        }        return res;    }    // 计算[0][0]到每一个方格的所有的数之和     // 比如  [1][1] 计算:[0][0], [1][0], [0][1], [1][1]        // [1][0] 计算:[0][0], [1][0]    private static int[][] get00ToSpecifiedGridSum(int[][] intArr) {        int[][] sum = new int[intArr.length][intArr[0].length];        // 计算sum矩阵的第二种方式        // 先初始化 [0][0], 单独计算第一行, 第一列        // 然后在计算之后的[row][col]        sum[0][0] = intArr[0][0];        for(int col=1; col<intArr[0].length; col++) {            sum[0][col] = sum[0][col-1] + intArr[0][col];        }        for(int row=1; row<intArr.length; row++) {            sum[row][0] = sum[row][0] + intArr[row][0];        }        for(int row=1; row<intArr.length; row++) {            sum[row][0] = sum[row-1][0] + intArr[row][0];            for(int col=1; col<intArr[0].length; col++) {                // 获取左边的一个方格的sum  在加上当前列([0-row][col])的数据  即为当前方格的sum                int tmp = sum[row][col-1];                for(int i=0; i<=row; i++) {                    tmp += intArr[i][col];                }                sum[row][col] = tmp;            }        }        return sum;    }    // arr中计算row, col为坐标, width, height为宽高的矩阵的和    private static int sum(int[][] arr, int row, int col, int width, int height) {        int sum = 0;        int maxRow = row + height, maxCol = col + width;        for(int i=row; i<maxRow; i++) {            for(int j=col; j<maxCol; j++) {                sum += arr[i][j];            }        }        return sum;    }}

5. 运行结果

这里写图片描述

6. 总结

第一种是完全的穷举, 第二种思路则是采用 使用时间换取空间的思路, 将计算矩阵的时间复杂度缩短为O(1), 第三种使用up, down 枚举了上下边界, 累加每一列之和 + findMaxSumSeq04 [O(m)] “枚举了左右边界”

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!

0 0
原创粉丝点击