首页 > 其他分享 >[53] Maximum Subarray

[53] Maximum Subarray

时间:2024-04-30 14:00:39浏览次数:23  
标签:最大 nums int Maximum 53 横跨 数组 Subarray left

算法助手

用户:

这题应该怎么做?Given an integer array nums, find the subarray with the largest sum, and return its sum.

ChatGPT:

这个问题是一个非常经典的算法问题,被称为最大子数组和问题,可以通过动态规划(Dynamic Programming)的方法高效解决。我们可以使用一个名为“Kadane算法”的技巧,这个算法的核心思想是遍历数组,同时计算当前最大子数组和,如果当前和变为负数,则从下一个元素重新开始计算。

以下是这个问题的C++实现:

#include <vector>
#include <algorithm>

using namespace std;

int maxSubArray(vector<int>& nums) {
    // 如果数组为空,直接返回0
    if (nums.empty()) return 0;

    int maxSum = nums[0]; // 初始化最大子数组和为数组的第一个元素
    int currentSum = nums[0]; // 初始化当前最大子数组和为数组的第一个元素

    // 从数组的第二个元素开始遍历
    for (int i = 1; i < nums.size(); i++) {
        // 如果currentSum加上当前元素的值比当前元素的值小,就抛弃之前的累加结果,从当前元素重新开始累加
        currentSum = max(nums[i], currentSum + nums[i]);
        // 更新最大子数组和
        maxSum = max(maxSum, currentSum);
    }

    return maxSum;
}

这段代码的核心在于两个max函数的使用:

  • 第一个max用于决定是继续累加当前子数组,还是从新的位置开始,这取决于currentSum + nums[i]nums[i]的比较结果。
  • 第二个max用于更新全局的最大子数组和。

这个算法的时间复杂度是O(n),其中n是数组nums的长度,因为我们只需要遍历一次数组。空间复杂度是O(1),因为我们只使用了常数级别的额外空间。

用户:

那么使用分而治之的方法呢?

ChatGPT:

使用分治法解决最大子数组和问题也是一种非常经典的方法。分治法的基本思想是将问题分解成更小的子问题来解决,然后将子问题的解合并以解决原问题。对于最大子数组和问题,我们可以将数组分成两半,最大子数组和可能出现在左半部分、右半部分,或者横跨两部分。因此,我们需要计算三种情况的最大子数组和,然后取这三者之中的最大值。

下面是使用分治法解决这个问题的C++代码:

#include <vector>
#include <algorithm>

using namespace std;

// 辅助函数,用于计算横跨两部分的最大子数组和
int crossSum(const vector<int>& nums, int left, int mid, int right) {
    if (left == right) return nums[left];

    int leftMaxSum = INT_MIN;
    int rightMaxSum = INT_MIN;
    int sum = 0;

    // 从中点向左扫描,找到最大子数组和
    for (int i = mid; i >= left; i--) {
        sum += nums[i];
        leftMaxSum = max(leftMaxSum, sum);
    }

    sum = 0; // 重置sum,用于从中点向右扫描
    // 从中点向右扫描,找到最大子数组和
    for (int i = mid + 1; i <= right; i++) {
        sum += nums[i];
        rightMaxSum = max(rightMaxSum, sum);
    }

    // 合并左右两部分的最大子数组和
    return leftMaxSum + rightMaxSum;
}

// 分治法主函数
int maxSubArrayDivideAndConquer(const vector<int>& nums, int left, int right) {
    if (left == right) return nums[left];

    int mid = (left + right) / 2;

    int leftMax = maxSubArrayDivideAndConquer(nums, left, mid);
    int rightMax = maxSubArrayDivideAndConquer(nums, mid + 1, right);
    int crossMax = crossSum(nums, left, mid, right);

    return max(max(leftMax, rightMax), crossMax);
}

int maxSubArray(vector<int>& nums) {
    return maxSubArrayDivideAndConquer(nums, 0, nums.size() - 1);
}

这个解决方案中,maxSubArrayDivideAndConquer函数递归地将数组分成更小的部分,直到不能再分(即数组只剩下一个元素)。crossSum函数计算横跨左右两部分的最大子数组和。最后,我们在每一层递归中比较左半部分、右半部分以及横跨两部分的最大子数组和,取三者中的最大值。

这种方法的时间复杂度是O(n log n),因为每次分解数组都需要线性时间来计算横跨两部分的最大子数组和,而分解过程是对数级别的。空间复杂度主要由递归调用栈的深度决定,为O(log n)。

用户:

也就是说,横跨的部分必然包含中点?

ChatGPT:

是的,正如你所理解的,当我们使用分治法处理最大子数组和问题时,考虑横跨两部分的子数组时,这个子数组必然包含中点。这是因为我们将原数组分为左右两部分,横跨两部分的最大子数组和的计算方式是基于一个假设:如果存在一个最大子数组横跨左右两部分,那么它必须包含左半部的某部分直至中点,以及中点之后右半部的某部分。

这个假设的直观理解是,如果我们在左半部找到一个最大子数组,它以中点或中点之前的某个位置结束,同时,在右半部找到一个最大子数组,它从中点或中点之后的某个位置开始,那么这两个子数组合并就可能形成一个新的、更大的子数组。因此,通过计算以中点结束的左半部最大子数组和以及以中点开始的右半部最大子数组,我们就能找到横跨两部分的最大子数组和。

具体到实现上,我们从中点向左扫描找到最大子数组和,然后从中点向右扫描找到最大子数组和,最后将这两个最大子数组和相加,即得到横跨左右两部分的最大子数组和。这个方法保证了无论横跨部分的最大子数组具体起止于何处,只要它包含中点,就能被正确计算出来。

标签:最大,nums,int,Maximum,53,横跨,数组,Subarray,left
From: https://www.cnblogs.com/Lemon-GPU/p/18167909

相关文章

  • P9753 [CSP-S 2023] 消消乐
    P9753[CSP-S2023]消消乐这题想到了50pts,想不出来怎么优化了。50pts:考虑枚举子串左端点,模拟操作过程,直接用栈模拟,遇到相同的则删去,如果某个时刻栈为空,那么合法子串数加一。考场上只想到为空的时候可消除,下面的性质才是关键的。因为我们枚举左端点,每次只判断了\([l,r]\)的......
  • Git runner 返回报错: status=couldn't execute POST against dial tcp: lookup gitlab
    当发现Gitlab上的runner显示出runneroffline的问题时1查一下gitrunner的报错runner=xxxxstatus=couldn'texecutePOSTagainsthttps://gitlab/api/v4/jobs/request:Posthttps://gitlab/api/v4/jobs/request:dialtcp:lookupgitonx.x.x.x:53:servermisbehaving......
  • P3953 [NOIP2017 提高组] 逛公园
    P3953[NOIP2017提高组]逛公园求有向图中\(1\)到\(n\)的路径中长度小于等于\(dis(1,n)+k\)的方案数。\(dis(1,n)\)表示最短路。\(k\le50\)。部分分\(k=0\),直接最短路计数即可。我们发现有向图中存在后效性,不好动态规划,但我们仔细思考后,在不存在\(0\)边的情况下,设......
  • CF1535F String Distance
    \(CF1535F\\String\Distance\)题意给\(n\)个长度均为\(len\)的字符串\(T_1,T_2,\dotsT_n\),定义\(f(a,b)\)为将\(a,b\)排序后相等的最小排序次数,若无解则为\(1337\)(这好像是个黑客用语)。求\[\sum_{i=1}^{n}\sum_{j=i+1}^{n}f(T_i,T_j)\]其中\[n\timeslen......
  • ITMS-91053 Missing API declaration
    热烈欢迎,请直接点击!!!进入博主AppStore主页,下载使用各个作品!!!注:博主将坚持每月上线一个新app!!今天上传应用发现谈了一大堆警告,对于警告洁癖的我表示非常的震惊。基本上就是因为缺少隐私描述,但是我根本就没用第三方SDK啊,仔细一看发现是这两种:NSPrivacyAccessedAPICategoryUserDe......
  • 推荐蓝牙对讲机内部PA+SW组合电路-CB5337+CBS8112
    CB5337是完整的2.4GHz802.11axWLANRF前端模块(FEM)。包含一个2.4GHz单刀双投(SPDT)发射/接收(T/R)开关,一个2.4GHz低噪声放大器(LNA),以及一个应用于大功率802.11ax2.4GHz功率放大器(PA),非标最高可提供33dbm发射增益和14dbm接收增益;CB5337提供了完整的2.4GHzWLAN射频解决方案,从......
  • 「杂题乱刷」AT_abc253_c
    感觉要好好补补set了。链接直接用set模拟即可。点击查看代码/*Tips:你数组开小了吗?你MLE了吗?你觉得是贪心,是不是该想想dp?一个小时没调出来,是不是该考虑换题?*/#include<bits/stdc++.h>usingnamespacestd;#definemapunordered_map#defineforl(i,a,b)for(re......
  • 洛谷 P3353 在你窗外闪耀的星星
    题意:给定一个宽度w,n个数,每个数有一个权值。窗口可以变换位置,求该窗口能容纳的最大权值。思路:前缀和+滑动窗口硬算。总结:一开始感觉是fenwicktree,但是每次查询的区间固定,不需要fenwicktree,不如滑动窗口来的方便。voidsolve(){intn,w;cin>>n>>w;vector<in......
  • 053、轮台歌奉送封大夫出师西征
    053、轮台歌奉送封大夫出师西征唐●岑参轮台城头夜吹角,轮台城北旄头落。羽书昨夜过渠黎,单于已在金山西。戍楼西望烟尘黑,汉兵屯在轮台北。上将拥旄西出征,平明吹笛大军行。四边伐鼓雪海涌,三军大呼阴山动。虏塞兵气连云屯,战场白骨缠草根。剑河风急云片阔,沙口石冻马蹄脱。亚......
  • 题解 LGP5397【[Ynoi2018] 天降之物】/ 第四分块
    题解P5397【[Ynoi2018]天降之物】/第四分块题目描述一个长为\(n\)的序列\(a\)。你需要实现\(m\)个操作,操作有两种:把序列中所有值为\(x\)的数的值变成\(y\)。找出一个位置\(i\)满足\(a_i=x\),找出一个位置\(j\)满足\(a_j=y\),使得\(|i-j|\)最小,并输出\(|i-......