1.题目
问题描述
小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
2.思路
穷举,找到所有的翻转方式,然后求最大子数组和假设通过翻转可以把所有正数放在一起,求所有正数的和即可,如果没有正数,答案为最大的负数- 初始化动态规划数组:
ldp
数组用于存储从左到右的最大子数组和。rdp
数组用于存储从右到左的最大子数组和。
- 计算从左到右的最大子数组和:
- 遍历数组,累加当前元素到
s
,如果s
小于 0,则重置s
为 0。 - 更新
ldp[i]
为ldp[i-1]
和s
的较大值,如果i
为 0,则直接取max(0, data_array[i])
。
- 遍历数组,累加当前元素到
- 计算从右到左的最大子数组和:
- 反向遍历数组,累加当前元素到
rdp[i]
,更新rdp[i]
为当前元素和rdp[i+1] + data_array[i]
的较大值,如果i
为N-1
,则直接取data_array[i]
。
- 反向遍历数组,累加当前元素到
- 计算翻转后的最大子数组和:
- 遍历数组,计算
ldp[i] + rdp[i+1]
的最大值,并与数组中的最大元素比较,取较大值作为结果。
- 遍历数组,计算
3.代码
思路二代码,经验证是错误的
#include <iostream>
#include <vector>
using namespace std;
int solution(int N, std::vector<int> data_array) {
// Please write your code here
bool has_positive = false;
int ans = 0;
int max_num = 0; //记录最大的数
for (int i = 0; i < data_array.size(); i++) {
max_num = max(max_num, data_array[i]);
if (data_array[i] > 0) {
ans += data_array[i];
has_positive = true;
}
}
if (has_positive == true) {
return ans;
} else {
return max_num;
}
}
int main() {
// You can add more test cases here
std::vector<int> array1 = {
1,2,3,-1,4
};
std::vector<int> array2 = {
-85, -11, 92, 6, 49, -76, 28, -16, 3, -29, 26, 37, 86, 3, 25,
-43, -36, -27, 45, 87, 91, 58, -15, 91, 5, 99, 40, 68, 54, -95,
66, 49, 74, 9, 24, -84, 12, -23, -92, -47, 5, 91, -79, 94, 61,
-54, -71, -36, 31, 97, 64, -14, -16, 48, -79, -70, 54, -94, 48, 37,
47, -58, 6, 62, 19, 8, 32, 65, -81, -27, 14, -18, -34, -64, -97,
-21, -76, 51, 0, -79, -22, -78, -95, -90, 4, 82, -79, -85, -64, -79,
63, 49, 21, 97, 47, 16, 61, -46, 54, 44};
std::cout << (solution(5, array1) == 10) << std::endl;
std::cout << (solution(100, array2) == 1348) << std::endl;
return 0;
}
def solution(N, data_array):
# Please write your code here
assert N == len(data_array)
ldp = [0] * N # 用于存储从左到右的最大子数组和
rdp = [0] * N # 用于存储从右到左的最大子数组和
s = 0 # 当前子数组和
for i in range(N):
s += data_array[i]
if s < 0:
s = 0 # 如果子数组和为负,重置为0
ldp[i] = max(ldp[i - 1], s) if i != 0 else max(0, data_array[i]) # 更新从左到右的最大子数组和
for i in range(N - 1, -1, -1):
rdp[i] = max(data_array[i], rdp[i + 1] + data_array[i]) if i != N - 1 else data_array[i]
ans = float('-inf')
for i in range(N - 1):
ans = max(ans, ldp[i] + rdp[i + 1]) # 计算翻转后的最大子数组和
return max(ans, max(data_array))
if __name__ == "__main__":
# You can add more test cases here
array1 = [-85, -11, 92, 6, 49, -76, 28, -16, 3, -29, 26, 37, 86, 3, 25, -43, -36, -27, 45, 87, 91, 58, -15, 91, 5, 99, 40, 68, 54, -95, 66, 49, 74, 9, 24, -84, 12, -23, -92, -47, 5, 91, -79, 94, 61, -54, -71, -36, 31, 97, 64, -14, -16, 48, -79, -70, 54, -94, 48, 37, 47, -58, 6, 62, 19, 8, 32, 65, -81, -27, 14, -18, -34, -64, -97, -21, -76, 51, 0, -79, -22, -78, -95, -90, 4, 82, -79, -85, -64, -79, 63, 49, 21, 97, 47, 16, 61, -46, 54, 44]
print(solution(5, [1,2,3,-1,4]) == 10 )
print(solution(100, array1) == 1348)
ldp[i] = max(ldp[i - 1], s) if i != 0 else max(0, data_array[i]) # 更新从左到右的最大子数组和
if i != 0
:判断是否是第一个元素。如果不是第一个元素,使用前一个元素的最大子数组和ldp[i - 1]
和当前子数组和s
来更新当前的最大子数组和。max(ldp[i - 1], s)
:ldp[i - 1]
代表前一个位置的最大子数组和,s
是当前子数组和。如果当前子数组和s
比前一个位置的最大和ldp[i - 1]
更大,就更新为s
。
else max(0, data_array[i])
:如果是第一个元素,ldp[0]
的值应该是data_array[0]
或者0
(如果data_array[0]
为负,则0
更好)。这意味着在处理第一个元素时,如果它为负数,我们选择跳过它,开始新的子数组。
rdp[i] = max(data_array[i], rdp[i + 1] + data_array[i]) if i != N - 1 else data_array[i]
if i != N - 1
:判断是否是最后一个元素。如果i != N - 1
,则说明当前位置不是数组的最后一个元素。此时,rdp[i]
可以是当前元素data_array[i]
本身,或者是将当前元素data_array[i]
与之后的子数组和rdp[i + 1]
结合,取这两者中的较大值。max(data_array[i], rdp[i + 1] + data_array[i])
:data_array[i]
:当前元素自身,表示如果选择从当前元素开始新的子数组(即舍弃之前的子数组)。rdp[i + 1] + data_array[i]
:表示将当前位置的元素加到之后的子数组和中,继续扩展当前的子数组。如果这样做会导致当前的子数组和更大,那么选择这个子数组。
else data_array[i]
:如果是最后一个元素(即i == N - 1
),则rdp[i]
只能是当前元素本身,因为没有更右边的元素可以加入。因此,rdp[N-1] = data_array[N-1]
。
ans = max(ans, ldp[i] + rdp[i + 1])
通过翻转子数组,可能将 ldp[i]
和 rdp[i + 1]
两个部分拼接起来,从而得到一个新的子数组。我们考虑从数组中选择一个区间,翻转该区间后,使得翻转后的子数组的和变得最大。具体步骤如下:
ldp[i]
表示左侧部分(从数组的开头到位置i
)的最大子数组和。rdp[i + 1]
表示右侧部分(从位置i + 1
到数组末尾)经过翻转后的最大子数组和。- 因此,
ldp[i] + rdp[i + 1]
表示翻转i
和i + 1
之间的部分后,得到的新的子数组和。
return max(ans, max(data_array))
确保函数返回的结果是最大可能的子数组和,即使最好的选择是不翻转任何子数组。以下是这行代码的详细解释:
max(data_array)
:这一部分找到数组中的最大单个元素。这在所有元素都为负数的情况下非常有用,因为此时最好的子数组和将是最大的单个元素。max(ans, max(data_array))
:这一部分将通过考虑所有可能的子数组翻转找到的最大子数组和(ans
)与数组中的最大单个元素进行比较。它确保函数返回最高的可能值。