问题描述:
小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]开端的子数组的最大子数组和就行了。
具体的细节和操作如下:
-
右侧最大子数组和 (
rightMax) :- 从右向左计算出以每个元素a[i]为界线(也就是说最大子数组和不一定包含这个a[i])的最大子数组的和,并储存在
rightMax数组中。 rightMax[i]的意义是,从第 i 个位置到数组末尾的最大子数组和。
- 从右向左计算出以每个元素a[i]为界线(也就是说最大子数组和不一定包含这个a[i])的最大子数组的和,并储存在
-
计算
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的最大值。
- 初始化
-
寻找答案:
- 通过遍历数组,从左到右计算另外一部分的最大子数组和
lTemp。 lTemp记录当前子数组和的最大值,初始化为 0。- 在每次遍历中,计算
lTemp + rightMax[i+1]的最大值,更新ans。
- 通过遍历数组,从左到右计算另外一部分的最大子数组和
-
结合翻转的逻辑:
- 通过翻转区间
[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)