leetcode:面试题 17.24. 子矩阵最大累加和

题目来源

题目描述

在这里插入图片描述

class Solution {
public:
    vector<int> getMaxMatrix(vector<vector<int>>& matrix) {

    }
};

题目解析

分析数据量

  • 1 <= matrix.length, matrix[0].length <= 200 1 0 8 / 1 0 2 = 1 0 6 , ( 1 0 2 ) 3 = 1 0 6 10^8 / 10^2 = 10^6,(10^2)^3 = 10^6 108/102=106(102)3=106,所以算法最大时间复杂度是 O ( N 3 ) O(N^3) O(N3),可以用dp来做

分析

由简入难,先从最简单的问题的入手吧:

给出一个整形数组,问最大子数组的和是多少?要求数组连续。

  • 输入样例:nums = [-3,4,-1,2,1,-5,4]
  • 输出样例:6,即连续子数组[4,-1,2,1]的和最大

思路:

可以DP,但是没有必要。可以这样理解:

  • 当之前已有的数组和大于0时,它必然对此时的和有贡献,因此在其基础上累加if sum > 0 : sum += nums[i]
  • 当之前已有的数组和小于0时,此时应该另起炉灶if sum < 0 : sum = nums[i]

代码如下:

int maxSubArray(vector<int> &nums) {
    if(nums.empty()){
        return 0;
    }
    
    int n = nums.size();
    int sum = nums[0];
    int max = nums[0];
    for (int i = 1; i < n; ++i) {
        sum = nums[i] + (sum > 0 ? sum : 0);
        max = std::max(max, sum);
    }
    
    return max;
}

如果想要的是最大数组位置而非最大数组和呢?

思路:

  • 仍然需要维护最大值max,数组和sum
  • 另外需要设置一个start表示数组的左边界,而右边界就是当前的位置 i i i,不用专门设置变量
  • 那么,start应该在何时更新呢?在由于sum小于0而另起炉灶时更新start

代码:

std::vector<int> maxSubArray(vector<int> &nums) {
    if(nums.empty()){
        return {};
    }

    std::vector<int> ans(2);
    int n = nums.size();
    int sum = nums[0];
    int max = nums[0];
    int start = 0;
    for (int i = 1; i < n; ++i) {
        if(sum >= 0){
            sum += nums[i];
        }else{
            sum = nums[i];
            start = 1;
        }
        
        if(sum > max){
            max = sum;
            ans[0] = start;
            ans[1] = i;
        }
    }

    return ans;
}

问题升维,当数组变成矩阵,如何找到这个最大值及其位置:

思路:我们将任意连续行合并为一行,不就变成了多个最大子数组问题了吗?

在这里插入图片描述

在这里插入图片描述
也就是将二维转换为一维矩阵:

  • 可以想象一块夹板,然后分别表示起始行、结束行
  • 可以在起始行和结束行找一个起始列和结束列,这样就确定了一个矩阵

思路:

  • 设置一个上边界top和一个下边界bottom,将之间的行进行合并([top, bottom]),尝试更新最大值;最终结果的"上下"即为当前的上下边界,"左右"在求解最大子数组问题时得出。
  • 在进行行的合并时,可以进行前缀和优化,即对每一列生成前缀和数组。

在这里插入图片描述

std::vector<int> maxSubArray(vector<std::vector<int>> &m) {
    int M = m.size(), N = m[0].size();
    int max = m[0][0];
    std::vector<int> ans(4);

    // 构造列的前缀和
    std::vector<std::vector<int>> preSum(M + 1, std::vector<int> (N));
    for (int i = 1; i < M + 1; ++i) {
        for (int j = 0; j < N; ++j) {
            preSum[i][j] = preSum[i - 1][j] + m[i - 1][j];
        }
    }

    // 合并行
    for (int top = 0; top < M; ++top) {
        for (int bottom = top; bottom < M; ++bottom) {
            // 构造一维矩阵
            std::vector<int> arr(N);
            for (int i = 0; i < N; ++i) {
                ans[i] = preSum[bottom + 1][i] - preSum[top][i];
            }

            // 最大子数组问题
            int start = 0;
            int sum = arr[0];
            for (int i = 1; i < N; ++i) {
                if(sum > 0){
                    sum += arr[i];
                }else{
                    sum = arr[i];
                    start = i;
                }
                
                if(sum > max){
                    max = sum;
                    ans[0] = top;
                    ans[1] = start;
                    ans[2] = bottom;
                    ans[3] = i;
                }
            }
        }
    }
    
    return ans;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值