前言&问题描述
最小子数和是一道经典的组合优化问题,我们通常碰到此题会想到使用动态规划的方法进行解决,以下为问题的描述:
(以下是取自于力扣53题:)
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
一、动态规划法
本人在碰到此题的时候,第一反应也是使用动态规划,这一题是典型的动态规划问题,代码如下:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
int currentMax = nums[0]; // 当前子数组的最大和
int globalMax = nums[0]; // 全局最大和
for (int i = 1; i < n; ++i) {
// 对于当前元素,考虑是否将其加入当前子数组
// 如果加入后当前子数组的和更大,则更新当前子数组的最大和
currentMax = max(nums[i], currentMax + nums[i]);
// 更新全局最大和
globalMax = max(globalMax, currentMax);
}
return globalMax;
}
};
在这段代码中,maxSubArray 函数接受一个整数数组 nums,然后使用动态规划来找到具有最大和的连续子数组,并返回其最大和。主要思想是遍历数组,对于每个元素,考虑将其加入当前子数组,如果加入后当前子数组的和更大,则更新当前子数组的最大和,并在全局范围内保持最大和的记录。最终,返回全局最大和即可。
二、分治法
在力扣上,这道题的末尾有这样一句话:
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
经过思考,我决定将问题这样解决:
1.将问题分成两个子问题:左半部分和右半部分,以数组的中间位置为界限。
2.分别递归地求解左半部分和右半部分的最大子数组和。
3.考虑包含中间位置的子数组,它的和是左半部分的最大后缀和与右半部分的最大前缀和的和。
4.返回三个值中的最大值:左半部分的最大子数组和、右半部分的最大子数组和以及包含中间位置的子数组和。
以下为解题代码:
class Solution {
public:
int solve(int left,int right,vector<int>& nums){
if(left==right){
return nums[left];
}
int mid=(right-left)/2+left;
int r=max(solve(left,mid,nums),solve(mid+1,right,nums));
int lsum=-1000000007,rsum=-1000000007;
int sum=0;
for(int i=mid;i>=left;i--){
sum+=nums[i];
if(sum>lsum)
lsum=sum;
}
sum=0;
for(int i=mid+1;i<=right;i++){
sum+=nums[i];
if(sum>rsum)
rsum=sum;
}
r=max(r,lsum+rsum);
return r;
}
int maxSubArray(vector<int>& nums) {
int left=0;
int right=nums.size()-1;
return solve(left,right,nums);
}
};
总结
通过这道题,我明白了动态规划和分治都是平时解决问题的算法范式,它们也具有一些相同点和不同点:
相同点:
- 问题分解:动态规划和分治都涉及将原始问题分解成更小的子问题。这种问题分解有助于将复杂问题转化为更容易解决的子问题。
- 递归:两者都通常使用递归来解决问题。通过递归,问题可以逐级分解,直到达到基本情况,然后逐级合并解决子问题的结果以获得原始问题的解。
不同点:
- 子问题重叠性:动态规划通常用于处理具有子问题重叠性的问题,即在解决问题时需要多次解决相同的子问题。为了避免多次计算相同的子问题,动态规划使用一个表格或数组来存储已经解决过的子问题的结果,以便以后直接查找。分治通常不关心子问题是否重叠,它只是将问题划分为子问题并独立地解决它们。
- 子问题的组合:分治将问题分解为子问题,并通过合并子问题的结果来解决原始问题。每个子问题都是独立解决的,没有相互依赖。在动态规划中,子问题的结果通常被组合和利用来解决更大的问题。动态规划问题通常涉及到最优化问题,需要在多个子问题中选择最佳策略。
- 时间复杂度:动态规划通常具有更低的时间复杂度,因为它避免了多次计算相同的子问题。分治则不关心子问题是否重叠,因此在某些情况下可能会导致更高的时间复杂度。
总的来说,动态规划更适用于具有子问题重叠性和需要组合子问题结果的问题,而分治更适用于将问题分割为独立子问题的情况,不需要重用子问题的结果。在选择算法时,需要根据问题的性质和要求来决定使用哪种方法。