每日一题,防止痴呆 = =
一、题目大意
给定一个整数数组 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;
}
};
如果有问题,欢迎大家指正!!!