非重叠子数组的最大和问题 | 豆包MarsCode AI 刷题

134 阅读4分钟

问题描述

小R遇到一个数组 nums,他需要从中找到两个非重叠的子数组,它们的长度分别为 firstLen 和 secondLen。这两个子数组可以相互独立,顺序没有限制,但它们不能有任何重叠。你需要帮小R找出这些子数组的最大和。


测试样例

样例1:

输入:nums = [0,6,5,2,2,5,1,9,4], firstLen = 1, secondLen = 2
输出:20

样例2:

输入:nums = [3,8,1,3,5,2,1,0], firstLen = 3, secondLen = 2
输出:21

样例3:

输入:nums = [2,1,4,3,5,9,5,0,3,8], firstLen = 4, secondLen = 3
输出:33

分析

这个函数 solution 的目的是找到两个不重叠子数组的最大和,其中第一个子数组的长度为 firstLen,第二个子数组的长度为 secondLen。下面是对这个函数的详细思路分析:

总体思路

  1. 前缀和计算:首先通过前缀和数组 cum_sum 来快速计算任意区间的和。
  2. 动态规划:使用动态规划来分别计算两个子数组的最大和,并将它们存储在 dp 数组中。
  3. 合并结果:最后通过遍历所有可能的位置,找到两个不重叠子数组的最大和。

具体步骤

1. 前缀和计算
  • 创建一个长度为 nums.length + 1 的数组 cum_sum,初始化为0。
  • 遍历 nums 数组,填充 cum_sum 数组,使得 cum_sum[i] 表示 nums 数组从第0个元素到第 i-1 个元素的和。

image.png

2. 动态规划
  • 创建一个二维数组 dp,其中 dp[0] 和 dp[1] 分别用于存储第一个子数组和第二个子数组的最大和。
  • 初始化 dp 数组的所有值为0。

image.png

  • 计算第一个子数组的最大和:

    • 对于每个位置 i,更新 dp[0][i] 为前 firstLen 个元素的最大和。
for (let i = firstLen; i <= nums.length; i++) {
    dp[0][i] = Math.max(dp[0][i - 1], cum_sum[i] - cum_sum[i - firstLen]);
}
Copy
  • 计算第二个子数组的最大和:

    • 对于每个位置 i,更新 dp[1][i] 为前 secondLen 个元素的最大和。
for (let i = secondLen; i <= nums.length; i++) {
    dp[1][i] = Math.max(dp[1][i - 1], cum_sum[i] - cum_sum[i - secondLen]);
}
3. 合并结果
  • 遍历所有可能的位置 i,找到两个不重叠子数组的最大和。

    • 对于每个位置 i,计算 dp[0][i] 加上从 i 到 i + secondLen 的和。
    • 对于每个位置 i,计算 dp[1][i] 加上从 i 到 i + firstLen 的和。
for (let i = firstLen; i <= nums.length - secondLen; i++) {
    max_sum = Math.max(max_sum, dp[0][i] + (cum_sum[i + secondLen] - cum_sum[i]));
}

for (let i = secondLen; i <= nums.length - firstLen; i++) {
    max_sum = Math.max(max_sum, dp[1][i] + (cum_sum[i + firstLen] - cum_sum[i]));
}

代码详解

涉及到的知识点

  • 前缀和:通过前缀和数组 cum_sum 快速计算任意区间的和。
  • 动态规划:使用动态规划方法来分别计算两个子数组的最大和。
  • 数组操作:创建和操作数组,包括初始化、填充和遍历。
  • 条件判断:使用 if 语句来控制程序流程。
  • 循环结构:使用 for 循环来遍历数组。
  • 函数定义与调用:定义函数并调用它。
  • 测试用例:编写测试用例来验证函数的正确性。

总结

通过青训营豆包MarsCode AI刷题,我学到了如何高效地运用多种算法和数据结构来解决实际问题。在前缀和的应用中,我掌握了快速计算数组中任意区间元素和的方法,极大地提高了算法效率。动态规划让我学会了如何定义状态、递推方程以及边界条件,从而有效解决优化问题。在数组操作方面,我熟练掌握了初始化、填充和遍历等基础操作,为解决大多数算法问题打下了坚实的基础。同时,通过条件判断和循环结构,我能够更好地控制程序的执行流程,解决复杂的逻辑问题。在函数定义与调用方面,我学会了如何合理地组织代码结构,提高代码的可读性和可维护性。最后,通过编写有效的测试用例,我能够验证算法的正确性,确保其在不同场景下的表现。总的来说,青训营的学习不仅提升了我的技术能力,还培养了我的问题分析和解决能力,希望我能学到更多的知识,提升自己。