翻转增益的最大子数组和
问题描述
小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
思路
分情况讨论:
①不翻转:计算原始数组的最大子数组和,用 Kadane算法; ②翻转:枚举所有可能的翻转子数组,将其拼接起来,利用Kadane算法来计算翻转后的数组的最大子数组和。 最后,比较哪个最大。
Kadane算法:
-
令数组的第一个数为当前子数组和max_end以及之前的子数组和max_so_far,从索引1的位置开始遍历数组,
-
判断当数加上下一个数,加上大 还是不加上大,并将其结果赋予当前的最大值;
-
判断 之前的数组和大,还是 当前的子数组和大,将其赋予之前的数组和,进入下一次循环中(会形成闭环)。
代码
def solution(N, data_array):
# ①不翻转的情况
max_sum_original = kadane(data_array)
max_sum = max_sum_original
# ②翻转的情况
# 选取最大的子数组和,i-j
for i in range(N):
for j in range(i,N):
# 翻转 i-j区间的数组,并利用 kadane算法求解最大子数组和
reverse_arr = data_array[i:j+1][::-1]
max_so_far = kadane(data_array[:i] + reverse_arr + data_array[j+1:])
max_sum = max(max_so_far,max_sum)
return max_sum
# kadane算法实现,
# - 找到最大子数组和。非连续也加上,加上然后比对大小
# - 存在问题:把不连续的正数也加上了
# - 解决:拆解:如何判断是否是连续的?不,神奇的是把两个式子改了过了:max_end = max(x,max_end+x); max_so_far = max(max_so_far,max_end).原因:循环了
def kadane(arr):
max_end = max_so_far = arr[0]
# 遍历arr数组
for x in arr[1:]:
# ★ 当前的数加上下一个数,判断加上还是不加上,哪个大,将它赋予当前最大值
max_so_far = max(x,max_so_far+x)
# ★ 判断 之前的子数组和 大 ,还是当前的子数组和大。将它赋予之前最大值,下次循环形成闭环。max_so_far把之前的数组和存起来了。
max_end = max(max_so_far,max_end)
return max_end
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(6, [5, -9, 6, 7, -6, 2]) ) #== 18 # 测试
# print(solution(7, [-8, -1, -2, -3, 4, -5, 6]) ) #== 10
print(solution(100, array1) ) #== 1348
知识点整理
-
python数组:
data_array[i:j+1][::-1],选取数组data_array的索引从i到j的部分(data_array[i:j+1]),然后 翻转数组([::-1])。
-
kadane算法:
Kadane算法是一种用于解决最大子数组和问题的动态规划问题。最大子数组问题,是指一个数组中找到一个连续的子数组,使得子数组的元素之和最大。
基本思想:遍历数组的每个元素,同时维护两个变量:
- 当前子数组(
max_so_far):表示以当前元素结尾的子数组的最大和。 - 全局最大和(
max_sum):表示已经遍历过的子数组的最大和。
遍历数组的过程中,对于每个元素,做以下操作:
- 将当前元素加入当前子数组中,从而扩展当前子数组。
- 以当前元素为起点开始一个新的子数组。
Kadane算法的关键在于 通过比较 当前元素 和 当前元素加上前一个元素的和 来更新当前子数组和。 具体步骤如下:
- 初始化
max_so_far和max_sum为数组中第一个元素的值。 - 从数组的第二个元素开始遍历:
- 更新
max_so_far为 当前元素 和max_so_far + 当前元素中的较大者。 - 更薪
max_sum为max_sum和max_so_far中的较大者。
- 更新
- 遍历完成后,
max_sum即为数组中的最大子数组和。
本算法的时间复杂度为O(n),是一种高效的解决方法。