翻转增益的最大子数组和| 豆包MarsCode AI 刷题

197 阅读4分钟

问题描述:

小C面对一个由整数构成的数组,他考虑通过一次操作提升数组的潜力。这个操作允许他选择数组中的任一子数组并将其翻转,目的是在翻转后的数组中找到具有最大和的子数组。小C对这个可能性很感兴趣,并希望知道翻转后的数组中可能得到的最大子数组和是多少。

例如,数组是 1, 2, 3, -1, 4。小C可以选择翻转子数组 -1, 4 得到 1, 2, 3, 4, -1 或者翻转 1, 2, 3, -1 得到 -1, 3, 2, 1, 4,在这两种情况下,最大的子数组和都是 10。

备注:子数组 是数组中的一个连续部分。

输入

  • N: 数组的长度
  • data_array: 一个长度为 N 的整数数组

输出

请你返回执行翻转操作后(也可以选择不翻转),数组中可能获得的最大子数组和。


测试样例:

样例1:

输入:N = 5,data_array = [1, 2, 3, -1, 4]
输出:10

样例2:

输入:N = 4,data_array = [-3, -1, -2, 3]
输出:3

样例3:

输入:N = 3,data_array = [-1, -2, -3]
输出:-1

样例4:

输入:N = 6,data_array = [-5, -9, 6, 7, -6, 2]
输出:15

样例5:

输入:N = 7,data_array = [-8, -1, -2, -3, 4, -5, 6]
输出:10

解题思路:

题目要求我们翻转一次后得到最大的子数组和,我们假设需要翻转的数组是由a[i],a[i+1]...,a[j-1],a[j]组成,那么在翻转之后,它可能和以a[j+1]开端的数组的子数组形成最大的子数组,也可能和a[i-1]为结尾的数组的子数组形成最大的子数组和。

其实这两种情况我们都可以化简成一种情况。就是将左边的数组进行翻转,与右边一段数组组合起来形成一个新的子数组,这个子数组的和就是最大的。

那我们该如何利用这个思路呢?假设区间[i,j]中存在一个k,使得[i,k]区间里的子数组的和是在所有k的选择里最大。 所以[k+1,j]这个区间是我们不需要的,我们另外还需要以a[j+1]开端的子数组的最大子数组和就行了。

具体的细节和操作如下:

  1. 右侧最大子数组和 (rightMax) :

    • 从右向左计算出以每个元素a[i]为界线(也就是说最大子数组和不一定包含这个a[i])的最大子数组的和,并储存在 rightMax 数组中。
    • rightMax[i] 的意义是,从第 i 个位置到数组末尾的最大子数组和。
  2. 计算 rightMax 的步骤:

    • 初始化 rightMax[n-1] 为 a[n-1],因为这是最右边的元素,最大和就是它本身。
    • 使用 rTemp 记录当前计算的子数组和,初始化为 a[n-1]
    • 从右往左更新 rTemp,每次计算当前元素加入后的和 rTemp + a[i] 和 a[i] 本身的最大值,这一步确保和a[i]本身比较。
    • 更新 rightMax[i] 为 rightMax[i+1] 和 rTemp 的最大值。
  3. 寻找答案:

    • 通过遍历数组,从左到右计算另外一部分的最大子数组和 lTemp
    • lTemp 记录当前子数组和的最大值,初始化为 0。
    • 在每次遍历中,计算 lTemp + rightMax[i+1] 的最大值,更新 ans
  4. 结合翻转的逻辑:

    • 通过翻转区间[i,j],最大化翻转后的子数组和,可以在第一个子数组 [i, k] 和第二个子数组 [j+1, n-1] 找到最大解。

代码中的关键细节

  • lTemp 在每次遍历时,用 Math.max(lTemp + a[i], 0) 保证左侧子数组和从零开始(即允许不选任何元素)。
  • ans 的更新结合了 lTemp 和 rightMax[i+1],这样就捕获了从翻转区间的左侧和右侧延伸的最大可能子数组和。

代码:

public static int solution(int n, int[] a) {
        int[] rightMax = new int[n];
        rightMax[n - 1] = a[n-1];
        int rTemp = a[n-1];
        
        for (int i = n - 2; i >= 0; --i) {
            rTemp = Math.max(rTemp + a[i], a[i]);
            rightMax[i] = Math.max(rightMax[i + 1], rTemp);
        }
        
        int ans = rightMax[0];
        int lTemp = 0;
        for (int i = 0; i < n - 1; ++i) {
            lTemp = Math.max(lTemp + a[i], 0);
            ans = Math.max(ans, lTemp + rightMax[i + 1]);
        }
        return ans;
    }

算法复杂度:

时间复杂度:O(n)

空间复杂度:O(n)