53. 最大子序和(C++)

题目

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

示例:

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

分析与题解

暴力循环

比较容易想到的是暴力解题,即穷举所有的子区间:

  • 使用双层循环,穷举所有的子区间;
  • 然后再对子区间内的所有元素求和;
  • 时间复杂度是立方级别

代码如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int max = INT_MIN;
        for(int i=0;i<n;i++){
            int sum=0;
            for(int j=i;j<n;j++){
                sum += nums[j];
                if(sum > max)
                    max = sum;
            }
        }
        return max;
    }
};

需要注意边界的问题:首先在外层循环遍历数组,作为求非连续数列的起点,然后从此出发逐个元素相加,每添加一个新的元素,sum值进行变化都需要与最终的结果进行比较,取较大值,最终一直添加到元素末位。

动态规划(状态转移方程1)

将问题切分为最优子问题进行求解。

①定义状态

我们使用一个数组或者哈希表dp存储子问题的答案。例如dp[i]代表以nums[i]结尾的连续子数组的最大和。

②思考转移状态方程

根据状态的定义,由于 nums[i] 一定会被选取。并且 dp[i]所表示的连续子序列与 dp[i - 1] 所表示的连续子序列(有可能)就差一个 nums[i]

先考虑最简单的情况,假设数组 nums 全是正数,那么一定有 dp[i] = dp[i - 1] + nums[i]

但在一般情况下 dp[i - 1] 有可能是负数,例如前几个数都是负数,突然来了一个正数。于是分类讨论:

  • 如果 dp[i - 1] >= 0,那么可以把 nums[i] 直接接在 dp[i - 1] 表示的那个数组的后面。
  • 如果 dp[i - 1] < 0,那么加上前面的数反而越来越小了,不如以nums[i]为起点作为dp[i]数值。

状态转移方程即为:

\[d p[i]=\left\{\begin{array}{ll} d p[i-1]+n u m s[i], & \text { if } \quad d p[i-1] \geq 0 \\ \text { nums }[i], & \text { if } \quad d p[i-1]<0 \end{array}\right.\]

代码如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        int sum = 0;
        int result = INT_MIN;
        for(int i=0;i<n;i++){
            sum+=nums[i];
            result = max(result, sum);
            if(sum < 0)
                sum = 0;
        }
        return result;
    }
};

自底向上(状态转移方程2)

对于dp[i-1]的讨论,最终无非是求子序列和的最大值,因此状态转移方程可以简写为:

\[d p[i]=\max \{n u m s[i], d p[i-1]+n u m s[i]\} \]

因此我们不再讨论dp[i-1]的正负,直接将是/否添加dp[i-1]的值比较并取较大值。代码如下:

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        vector<int> dp(n);
        dp[0] = nums[0];
        int result = dp[0];
        for(int i=1;i<n;i++){
            dp[i] = max(dp[i-1]+nums[i], nums[i]);
            result = max(result, dp[i]);
        }
        return result;
    }
};

需要注意的是状态dp代表的是以nums[i]结尾的子序列和的最大值,但是对于所给序列的最大值并不一定以包含末尾元素,所以需要设置额外的变量来进行记录和比较。

posted @ 2020-09-13 20:13  脱线森林`  阅读(369)  评论(0编辑  收藏  举报