【每日一题】LeetCode. 53. 最大子序和

每日一题,防止痴呆 = =

一、题目大意

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
在这里插入图片描述
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-subarray

二、题目思路以及AC代码

本来原题没有什么,无非是求最大的子序和,用dp的方法就可以解决,考虑dp[i]表示以第i个数作为结尾,最大的子序和是多少,那么递推公式如下:
在这里插入图片描述
意思是,在求解dp[i]时,我们考虑两种可能,一种可能是把第i个数加入以第i-1个数作为结尾的最大子序和的序列中,那样的话最大子序和应该是dp[i-1] + nums[i],还有一种可能是直接抛弃之前的,以第i个数自己作为新的最大子序和的序列,二者去大即可,然后最终结果就是dp数组中最大的那个数。

用上述方法求解的时间复杂度是O(n),但题目给了一个进阶要求,即考虑用分治来解决,因为之前没有看过线段树,所以就去看了题解,其实是线段树中的一个思想。

我们可以设置对每个子区间 [l, r] 求解四个值:l_sum, r_sum, i_sum, m_sum。

l_sum表示以 l 为左端点的序列的最大子序和
r_sum表示以 r 为右端点的序列的最大子序和
i_sum表示该区间的和
m_sum表示该区间的最大子序和

这样的话,我们只要可以定义这些值如何分治求解(即怎么通过左右区间的值,合并为整个区间的值),那么我们就可以得到最终的结果了,只要设置一个函数get_max_sequence(),递归的求解就行了,下面定义左区间为left,右区间为right

i_sum比较容易,直接用左右区间的i_sum求和即可,i_sum = left.i_sum + right.i_sum。
l_sum是求取左区间的l_sum和左区间i_sum + 右区间l_sum的最大值,l_sum = max(left.l_sum, left.i_sum + right.l_sum)。
r_sum是求取右区间的r_sum和右区间i_sum +左区间r_sum的最大值,r_sum = max(right.r_sum, right.i_sum + left.r_sum)。
m_sum是三者的最大值,分别是左区间的m_sum,右区间m_sum,以及左区间r_sum + 右区间l_sum,m_sum = max(left.m_sum, right.m_sum, left.r_sum + right.l_sum)。

关于m_sum的求解可以考虑取得最大子序和的序列是否经过mid值,不经过的话就是前两种情况,要么左边,要么右边,如果经过的话,就是最后一种情况。

下面给出AC代码:

DP:

#define INF 2147483647

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n_size = nums.size();
        if (n_size == 0) return 0;

        int dp[n_size];
        dp[0] = nums[0];
        
        for (int i=1;i<n_size;i++) {
            dp[i] = max(dp[i-1] + nums[i], nums[i]);
        }

        int res = -INF;
        for (int i=0;i<n_size;i++) {
            res = max(res, dp[i]);
        }

        return res;
    }
};

分治:

#define INF 2147483647

class Solution {
public:
    struct Status {
        int l_sum, r_sum, i_sum, m_sum;

        Status() {}

        Status(int l_sum, int r_sum, int i_sum, int m_sum) {
            this->l_sum = l_sum;
            this->r_sum = r_sum;
            this->i_sum = i_sum;
            this->m_sum = m_sum;
        }
    };

    Status get_max_sequence(vector<int>& nums, int l, int r) {
        if (l == r) return Status(nums[l], nums[l], nums[l], nums[l]);

        int mid = (l + r) >> 1;
        Status left = get_max_sequence(nums, l, mid);
        Status right = get_max_sequence(nums, mid + 1, r);

        Status res;
        res.l_sum = max(left.l_sum, left.i_sum + right.l_sum);
        res.r_sum = max(right.r_sum, right.i_sum + left.r_sum);
        res.i_sum = left.i_sum + right.i_sum;
        res.m_sum = max(left.m_sum, max(right.m_sum, left.r_sum + right.l_sum));

        return res;
    }

    int maxSubArray(vector<int>& nums) {
        Status res = get_max_sequence(nums, 0, nums.size() - 1);
        return res.m_sum;
    }
};

如果有问题,欢迎大家指正!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值