DP·翻转增益的最大数组和 | 豆包MarsCode AI刷题

80 阅读5分钟

翻转增益的最大子数组和

问题描述

小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算法是一种用于解决最大子数组和问题的动态规划问题。最大子数组问题,是指一个数组中找到一个连续的子数组,使得子数组的元素之和最大。

基本思想:遍历数组的每个元素,同时维护两个变量:

  1. 当前子数组(max_so_far):表示以当前元素结尾的子数组的最大和。
  2. 全局最大和(max_sum):表示已经遍历过的子数组的最大和。

遍历数组的过程中,对于每个元素,做以下操作:

  1. 将当前元素加入当前子数组中,从而扩展当前子数组
  2. 以当前元素为起点开始一个新的子数组

Kadane算法的关键在于 通过比较 当前元素 和 当前元素加上前一个元素的和 来更新当前子数组和。 具体步骤如下:

  1. 初始化 max_so_farmax_sum为数组中第一个元素的值。
  2. 从数组的第二个元素开始遍历:
    • 更新max_so_far为 当前元素 和 max_so_far + 当前元素中的较大者。
    • 更薪 max_summax_summax_so_far中的较大者。
  3. 遍历完成后,max_sum即为数组中的最大子数组和。

本算法的时间复杂度为O(n),是一种高效的解决方法。

参考:一文刷题学懂(Kadane算法)——java+python——1/2-CSDN博客