算法——求矩阵中和最大的子矩阵

给定一个正整数、负整数和 0 组成的 N × M 矩阵,编写代码找出元素总和最大的子矩阵。

返回一个数组 [r1, c1, r2, c2],其中 r1, c1 分别代表子矩阵左上角的行号和列号,r2, c2 分别代表右下角的行号和列号。若有多个满足条件的子矩阵,返回任意一个均可。
leetcode

解题思路:

  • 首先为了在O(1)的时间内获取两个点之间的矩阵和,就需要求每个点的前缀和,然后通过前缀和的是运算,快速获取两个点构成的矩阵的和。
  • 然后是遍历整个数组,但是如果依次枚举每两个点的话,时间复杂度是n的四次方的,会超时。所以,这里需要利用动态规划的思想。如果让一个维度不变,就可以把问题变为一维数组求和最小的连续字串问题,当前缀小于零,那就只会给后面添累赘,所以直接就去掉。
class Solution {
    public int[] getMaxMatrix(int[][] matrix) {
        int n = matrix.length;
        if(n == 0) return new int[0];
        int m = matrix[0].length;

        int[][] f = new int[n + 1][m + 1];
        int[][] s = new int[n + 1][m + 1];

		// 求二维的前缀和
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= m; j++) {
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + matrix[i - 1][j - 1];
            }
        }

        int[] res = new int[4];
        int sum = matrix[0][0];

		// 先枚举一个维度,再枚举另一个维度,这样就将为一维数组求和最大的连续子串问题。
        for(int i = 1; i < n + 1; i++) {
            for(int j = 0; j < i; j++) {
                int start = 0;
                for(int k = 1; k < m + 1; k ++) {
                    int cur = s[i][k] - s[j][k] - s[i][start] + s[j][start];
                    if(cur > sum) {
                        sum = cur;
                        res = new int[]{j, start, i - 1, k - 1};
                    }
					// 如果前面的和小于零,只会拖累后面,索性直接去掉前面的
                    if(cur < 0) start = k;
                }
            }
        }

        return res;
    }
}
posted @ 2020-11-27 19:17  lippon  阅读(774)  评论(0编辑  收藏  举报