Leetcode 面试题 17.24. 最大子矩阵

该博客讨论了一个寻找给定矩阵中元素总和最大的子矩阵的问题。使用动态规划和前缀和的方法,可以在O(n^3)的时间复杂度内找到答案。文章详细介绍了算法的实现过程,包括如何将二维问题转化为一维,并通过三层循环找到最佳的行和列范围。最后,给出了C++代码实现。
摘要由CSDN通过智能技术生成

本题链接

题目

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

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

注意:本题相对书上原题稍作改动

示例:

输入:
[
   [-1,0],
   [0,-1]
]
输出:[0,1,0,1]
解释:输入中标粗的元素即为输出所表示的矩阵

说明:

1 <= matrix.length, matrix[0].length <= 200

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-submatrix-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


思路

一维数组上,元素总和最大的连续子数组这个问题能用动态规划在 O ( n ) O(n) O(n) 的时间复杂度求解,那么现在就要把二维的问题转化成一维

对于二维的范围,假设子矩阵左上角的元素下标是 ( r o w 1 , c o l 1 ) (row1,col1) (row1,col1),子矩阵右下角的元素下标是 ( r o w 2 , c o l 2 ) (row2,col2) (row2,col2),那么子矩阵行的范围就是 [ r o w 1 , r o w 2 ] [row1,row2] [row1,row2],列的范围是 [ c o l 1 , c o l 2 ] [col1,col2] [col1,col2]。如果先用两层循环来枚举所有可能的行范围,在每一次两层循环内部,我们要做的事情就是求解出最佳的列范围。而如果行范围是已知的,我们就可以把 [ r o w 1 , r o w 2 ] [row1,row2] [row1,row2] 行中的元素在竖直方向上进行压缩成一维数组,数组中元素 nums[i] 的值表示矩阵在第 i i i 列上, [ r o w 1 , r o w 2 ] [row1,row2] [row1,row2] 行所有元素的和,这就是一开始已知的问题,所以一共是三层循环,在每次得到最优解的时候维护 ans 的值即可

最后还有一个问题就是:如何快速得到压缩后的一维数组?这里首先遍历矩阵,得到矩阵在列方向上的前缀和,然后每次在两层循环内部用减法就能得到待处理的一维数组

时间复杂度: O ( n 3 ) O(n^3) O(n3)
空间复杂度: O ( n 2 ) O(n^2) O(n2)

C++ 代码

class Solution {
public:
    vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
        if (matrix.empty() || matrix[0].empty())
            return vector<int>();
        const int Y = matrix.size();
        const int X = matrix[0].size();
        // 得到列方向上的前缀和矩阵prefixSumCol
        vector<vector<int>> prefixSumCol(Y, vector<int>(X, 0));
        for (int x = 0; x < X; ++x) {
            prefixSumCol[0][x] = matrix[0][x];
            for (int y = 1; y < Y; ++y) {
                prefixSumCol[y][x] = prefixSumCol[y - 1][x] + matrix[y][x];
            }
        }
        
        int maxSum = matrix[0][0];
        vector<int> ans(4, 0);
        //对每一个行范围[i,j],寻找最优的列范围
        for (int i = 0; i < Y; ++i) {
            for (int j = i; j < Y; ++j) {
                vector<int> sumsOfCol(X);
                if (i == 0) {
                    for (int k = 0; k < X; ++k)
                        sumsOfCol[k] = prefixSumCol[j][k];
                } else {
                    for (int k = 0; k < X; ++k)
                        sumsOfCol[k] = prefixSumCol[j][k] - prefixSumCol[i - 1][k];
                }
                
                vector<int> dp(X);
                dp[0] = sumsOfCol[0];
                int begin = 0;
                if (dp[0] > maxSum)
                    ans = vector<int>{i, 0, j, 0};
                for (int k = 1; k < X; ++k) {
                    if (dp[k - 1] < 0) {
                        dp[k] = sumsOfCol[k];
                        begin = k;
                        if (dp[k] > maxSum) {
                            maxSum = dp[k];
                            ans = vector<int>{i, k, j, k};
                        }
                    } else {
                        dp[k] = dp[k - 1] + sumsOfCol[k];
                        if (dp[k] > maxSum) {
                            maxSum = dp[k];
                            ans = vector<int>{i, begin, j, k};
                        }
                    }
                }
            }
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值