刷题日记 最大连续子数组和类型题目总结|豆包MarsCode AI刷题

70 阅读5分钟

最大连续数组和等类型刷题总结

最近一直碰到这些求最大连续子数组最大和的题目。简而言之就是给定一个数组A=[-1,1,2],求数组A的连续子数组的最大和,例如[a2,a3]就是其中A的连续子数组的最大和。
按照我们最快速能想到的方法,我们可以用暴力求解求出。

暴力求解

  1. 遍历所有可能的子数组A[i:j],i和j到遍历到n-1
  2. 求出每个子数组和的最大值,用一个变量存储
    maxSum = float('-inf')
    
    # 不修改任何元素,计算最大子数组和
    for i in range(n):
        currentSum = 0
        for j in range(i, n):
            currentSum += a[j]
            maxSum = max(maxSum, currentSum)

不足

  1. 暴力求解的时间复杂度o(n^2),遇到长数组运行时间长。
  2. 进行了重复的无意义的比较,比如[-1,2,3,1],完全没必要考虑包含有-1的子数组

Kadane算法

这个算法的核心就是计算每个以(0,n)中的每个索引i为结尾的最大连续子数组和

具体过程

  1. 初始化: 维护当前子数组的最大和 maxEndingHere 和全局子数组的最大和 maxSoFar
  2. 对于每个元素,计算在当前位置结束的最大子数组和: maxEndingHere = max(nums[i], maxEndingHere + nums[i]); 这表示要么继续当前子数组,要么从当前位置开始一个新的子数组。
  3. 更新全局最大子数组和: maxSoFar = max(maxSoFar, maxEndingHere);
    如果在当前位置结束的子数组和大于全局最大和,更新全局最大和。
  4. 返回结果: 当迭代完成后,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)

总结

  1. 实际上卡丹算法就是动态规划的方法,把问题拆解为子问题求每个以i为索引的最大子数组和。
  2. 实际题目可能会对一些数组进行修改,再让我们求最大子数组和,这个时候因题目而异