【专题】最大子数组和 / 子矩阵和

本文详细介绍了如何解决寻找数组或矩阵中最大子数组和与最大子矩阵和的问题,包括动态规划的解决方案,以及如何在返回最大和的同时获取子数组或子矩阵的起始和结束位置。讨论了不同情况下的时间复杂度,并提供了相关题解链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 目录

最大子序和 (最大子数组和)

最大子序和 (返回子数组位置)

最大子矩阵

最大子矩阵(返回矩阵坐标)

环状矩阵的最大子矩阵和


最大子序和 (最大子数组和)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

【思路】动态规划,O(n)

状态定义:dp[i]为以nums[i]结尾的最大子序和

状态转移方程:dp[i] = max( dp[i-1] + nums[i] , nums[i] ) 

由于dp[i] 只和 前一个状态dp[i-1] 及当前元素有关,所以可不必定义dp数组

int maxSubArray(vector<int>& nums) {
    if(nums.size()==0) return 0;
    int sum = 0, max_sum = nums[0];     // max_sum不能初始化为0, 以防数组只有负数
    for(int num : nums){
        if(sum>0)
            sum += num;
        else sum = num;
        
        if(sum>max_sum) max_sum = sum;
    }
    return max_sum;
}

 

最大子序和 (返回子数组位置)

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回它的开始、末尾元素的下标。

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: [3,6]
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

【思路】与上题唯一不同是 返回类型,所以同样是动态规划,只是在遍历过程中要记录当前子数组的开始下标。时间O(n)

vector<int> maxSubArray(vector<int>& nums) {
    int maxsum = INT_MIN;
    int dp_i = nums[0];
    vector<int> ans(2);    //用来记录答案
    int begin = 0;

    for(int i=1 ; i < nums.size() ; i++ ){
        if( dp_i > 0 )     //dp[i-1]>0时
            dp_i+=nums[i];
        else{              //dp[i-1]<=0时
            dp_i=nums[i];
            begin = i;     //当nums[i]自立门户时候,我们记录下子序列的起始位置
        }
        if(dp_i > maxsum){  //更新答案
            maxsum = dp_i;
            ans[0] = begin; //记录下起始和终止位置
            ans[1] = i;
        }
    }
    return ans;
}

 

最大子矩阵

给定一个正整数和负整数组成的 N × M 矩阵,找出元素总和最大的子矩阵,返回它的元素总和。

输入:
   [1,3,-4,2],
   [2,-1,2,-1]
输出: 5

【思路】 这题可转化为"最大子数组和"求解,只需要将二维转化为一维。

对于矩阵的每一列,我们将其加在一起,得到一维数组 b,b[i]表示矩阵第 i 列所有元素和。更准确的解释如下图:

捕获.JPG

那么怎么把每个子矩阵都遍历到呢?我们每次选择两行 i 和 j,代表子矩阵的上下边界, 并求每列的和(得到数组b),然后按"最大子数组和"的方法求数组 b 的最大子数组和。

时间复杂度:O(m*n^2),其中遍历 i 和 j 需要O(n^2),再执行"最大子数组和"方法需要O(m)。

(参考题解

int getMaxMatrix(vector<vector<int>>& matrix) {
    vector<int> ans(4);     //保存最大子矩阵的左上角和右下角的坐标
    int N = matrix.size();
    int M = matrix[0].size();
    vector<int> b(M,0);     //记录当前i~j行组成的子矩阵的每列的和,b[k]表示a[i][k]到a[j][k]的和
    int sum;                //相当于dp[i]
    int maxsum = INT_MIN;     //记录最大值

    for(int i=0;i<N;i++){       // i为子矩阵的上边界,从上而下扫描
        for(int t=0;t<M;t++)
            b[t]=0;             //每次改变子矩阵上边界,就要清空b,重新计算每列的和
        for(int j=i;j<N;j++){   // j为子矩阵的下边界,遍历一次就相当于求一次最大子序列和
            sum = 0;            //重新求dp
            for(int k=0;k<M;k++){
                b[k] += matrix[j][k];

                if(sum>0) sum += b[k];
                else sum = b[k];

                if(sum > maxsum)  // 若当前子矩阵的和更大,则用它更新答案
                    maxsum = sum;
            }
        }
    }
    return maxsum;
}

 

最大子矩阵(返回矩阵坐标)

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

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

【与上题唯一不同是 返回类型】

输入:
   [1,3,-4,2],
   [2,-1,2,-1]
输出: [0,0,1,1]

【思路】和上题思路基本一样,只是在遍历过程中要更新当前最大子矩阵的坐标。 

时间复杂度:O(m*n^2),其中遍历 i 和 j 需要O(n^2),再执行"最大子数组和"方法需要O(m)。

(参考题解

vector<int> getMaxMatrix(vector<vector<int>>& matrix) {
    vector<int> ans(4);     //保存最大子矩阵的左上角和右下角的坐标
    int N = matrix.size();
    int M = matrix[0].size();
    vector<int> b(M,0);//记录当前i~j行组成的子矩阵的每列的和,b[k]表示a[i][k]到a[j][k]的和
    int sum;                //相当于dp[i]
    int maxsum=INT_MIN;     //记录最大值
    int best_r1,best_c1;    //暂时记录左上角,相当于最大子数组问题中的begin

    for(int i=0;i<N;i++){     // i为子矩阵的上边界,从上而下扫描
        for(int t=0;t<M;t++ ) b[t]=0;    //每次改变子矩阵上边界,就要清空b,重新计算每列的和
        for(int j=i;j<N;j++){    // j为子矩阵的下边界,遍历一次就相当于求一次最大子序列和
            sum = 0;    //重新求dp
            for(int k=0;k<M;k++){
                b[k] += matrix[j][k];

                if(sum>0)
                    sum+=b[k];
                else{
                    sum=b[k];
                    best_r1=i;      //自立门户,暂时保存其左上角
                    best_c1=k;
                }

                if(sum > maxsum){  // 若当前子矩阵的和更大,则用它更新答案
                    maxsum = sum;
                    ans[0]=best_r1;
                    ans[1]=best_c1;
                    ans[2]=j;
                    ans[3]=k;
                }
            }
        }
    }
    return ans;
}

 

环状矩阵的最大子矩阵和 

猿辅导笔试题 20.8.22

【代码】https://www.nowcoder.com/discuss/485790

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值