最大子数和的一题多解(动态规划,分治)


前言&问题描述

最小子数和是一道经典的组合优化问题,我们通常碰到此题会想到使用动态规划的方法进行解决,以下为问题的描述:

(以下是取自于力扣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); 
    }
};

总结

通过这道题,我明白了动态规划和分治都是平时解决问题的算法范式,它们也具有一些相同点和不同点:

相同点:

  1. 问题分解:动态规划和分治都涉及将原始问题分解成更小的子问题。这种问题分解有助于将复杂问题转化为更容易解决的子问题。
  2. 递归:两者都通常使用递归来解决问题。通过递归,问题可以逐级分解,直到达到基本情况,然后逐级合并解决子问题的结果以获得原始问题的解。

不同点:

  1. 子问题重叠性:动态规划通常用于处理具有子问题重叠性的问题,即在解决问题时需要多次解决相同的子问题。为了避免多次计算相同的子问题,动态规划使用一个表格或数组来存储已经解决过的子问题的结果,以便以后直接查找。分治通常不关心子问题是否重叠,它只是将问题划分为子问题并独立地解决它们。
  2. 子问题的组合:分治将问题分解为子问题,并通过合并子问题的结果来解决原始问题。每个子问题都是独立解决的,没有相互依赖。在动态规划中,子问题的结果通常被组合和利用来解决更大的问题。动态规划问题通常涉及到最优化问题,需要在多个子问题中选择最佳策略。
  3. 时间复杂度:动态规划通常具有更低的时间复杂度,因为它避免了多次计算相同的子问题。分治则不关心子问题是否重叠,因此在某些情况下可能会导致更高的时间复杂度。

总的来说,动态规划更适用于具有子问题重叠性和需要组合子问题结果的问题,而分治更适用于将问题分割为独立子问题的情况,不需要重用子问题的结果。在选择算法时,需要根据问题的性质和要求来决定使用哪种方法。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值