最大连续数组和等类型刷题总结
最近一直碰到这些求最大连续子数组最大和的题目。简而言之就是给定一个数组A=[-1,1,2],求数组A的连续子数组的最大和,例如[a2,a3]就是其中A的连续子数组的最大和。
按照我们最快速能想到的方法,我们可以用暴力求解求出。
暴力求解
- 遍历所有可能的子数组
A[i:j],i和j到遍历到n-1 - 求出每个子数组和的最大值,用一个变量存储
maxSum = float('-inf')
# 不修改任何元素,计算最大子数组和
for i in range(n):
currentSum = 0
for j in range(i, n):
currentSum += a[j]
maxSum = max(maxSum, currentSum)
不足
- 暴力求解的时间复杂度o(n^2),遇到长数组运行时间长。
- 进行了重复的无意义的比较,比如[-1,2,3,1],完全没必要考虑包含有-1的子数组
Kadane算法
这个算法的核心就是计算每个以(0,n)中的每个索引i为结尾的最大连续子数组和
具体过程
- 初始化:
维护当前子数组的最大和
maxEndingHere和全局子数组的最大和maxSoFar。 - 对于每个元素,计算在当前位置结束的最大子数组和:
maxEndingHere = max(nums[i], maxEndingHere + nums[i]); 这表示要么继续当前子数组,要么从当前位置开始一个新的子数组。 - 更新全局最大子数组和:
maxSoFar = max(maxSoFar, maxEndingHere);
如果在当前位置结束的子数组和大于全局最大和,更新全局最大和。 - 返回结果:
当迭代完成后,
maxSoFar中存储的即为最大子数组和。
证明过程
具体可以看b站上面的视频,卡丹算法求解最大连续数组和
类型题目
困难题 最大连续子数组和问题
问题描述
小C拿到了一个数组,他可以进行最多一次操作:将一个元素修改为任意给定的xx。小C想知道,经过这次修改后,能够得到的连续子数组的最大和是多少。
测试样例
样例1:
输入:
n = 5 ,x = 10 ,a = [5, -1, -5, -3, 2]
输出:15
样例2:
输入:
n = 2 ,x = -3 ,a = [-5, -2]
输出:-2
样例3:
输入:
n = 6 ,x = 10 ,a = [4, -2, -11, -1, 4, -1]
输出:15
答案
核心思想:修改数组的每一个元素,计算修改后的最大连续习数组和,比较所有的情况得出答案。用了卡丹算法后真的很快很简洁
def solution(n: int, x: int, a: list) -> int:
# write code here
MaxG = Dane(a, n)+x-max(a)
for i in range(n):
tmp = a[i]
a[i] = x
MaxG = max(Dane(a, n), MaxG)
a[i] = tmp
return MaxG
def Dane(a: list, n: int) -> int:
maxC = a[0]
maxG = a[0]
for i in range(1, n):
maxC = max(a[i], a[i]+maxC)
maxG = max(maxG, maxC)
return maxG
if __name__ == '__main__':
print(solution(n=5, x=10, a=[5, -1, -5, -3, 2]) == 15)
print(solution(n=2, x=-3, a=[-5, -2]) == -2)
print(solution(n=6, x=10, a=[4, -2, -11, -1, 4, -1]) == 15)
翻转增益的最大子数组和
问题描述
小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
答案
核心过程:同理,计算所有翻转后的情况,对每一个情况用卡丹算法求最大连续子数组和,求出答案。
def solution(N, data_array):
max_sum = float('-inf')
for i in range(len(data_array)):
for j in range(i, len(data_array)):
flipped_array = data_array[:i] + \
data_array[i:j + 1][::-1] + data_array[j + 1:]
current_sum = max_subarray_sum(flipped_array)
max_sum = max(max_sum, current_sum)
return max_sum
def max_subarray_sum(arr):
max_end = max_sum = arr[0]
for num in arr[1:]:
max_end = max(num, max_end + num)
max_sum = max(max_sum, max_end)
return max_sum
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)
总结
- 实际上卡丹算法就是动态规划的方法,把问题拆解为子问题求每个以i为索引的最大子数组和。
- 实际题目可能会对一些数组进行修改,再让我们求最大子数组和,这个时候因题目而异