刷题进展之初等题-翻转增益的最大子数组和 | 豆包MarsCode AI刷题

3 阅读6分钟

问题描述

小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

下面我们先来分析一下题目:

题意分析

  1. 数组及操作定义
    题目给定了一个由整数构成的数组,并且定义了一种操作,即可以选择数组中的任意一个子数组进行翻转。这里的子数组是数组中的连续部分,例如对于数组 [1, 2, 3, -1, 4],像 [2, 3][-1, 4] 等都是合法的子数组。操作的目的就是通过选择合适的子数组进行翻转,然后在翻转后的数组里找到具有最大和的子数组。
  2. 输出要求
    需要返回执行了上述翻转操作后(当然也可以选择不翻转,也就是原始数组的情况也要考虑在内),整个数组中可能获得的最大子数组和。这意味着要遍历所有可能的子数组翻转情况,计算每种情况下的最大子数组和,最后从中找出最大值作为最终的答案。
  3. 样例说明
    通过给出的几个测试样例可以更直观地理解题意。比如样例 1 中数组 [1, 2, 3, -1, 4],展示了两种不同的翻转子数组的方式以及对应的结果,最终最大子数组和是 10。每个样例都是在不同的数组场景下,考察能否准确找出经过合适翻转后能得到的最大子数组和这一关键结果。

解题步骤

  1. 不翻转的情况(计算原始数组最大子数组和)

    • 可以使用经典的动态规划方法(例如 “最大子数组和问题” 的标准解法)来计算原始数组的最大子数组和。
    • 定义一个变量 cur_sum 用于记录当前位置的子数组和,初始化为数组的第一个元素 data_array[0],再定义一个变量 max_sum 用于记录全局最大子数组和,初始值也设为 data_array[0]
    • 从数组的第二个元素开始遍历(索引为 1 ),对于每个元素 data_array[i],计算 cur_sum = max(data_array[i], cur_sum + data_array[i]),意思是当前位置的子数组和要么是当前元素自己(表示从这个元素重新开始一个子数组),要么是包含上一个位置的子数组和再加上当前元素(表示延续之前的子数组)。然后更新 max_sum = max(max_sum, cur_sum),确保 max_sum 始终记录着到当前位置为止出现过的最大子数组和。
    • 遍历完整个数组后,max_sum 就是原始数组的最大子数组和,将这个值记为 original_max,它是后续比较的一个基础值,毕竟不翻转也是一种可能情况。
  2. 遍历所有可能的子数组进行翻转并计算最大子数组和

    • 使用两层嵌套循环来枚举所有可能的子数组。外层循环控制子数组的起始位置 start,从 0 开始遍历到 N - 1N 是数组长度);内层循环控制子数组的结束位置 end,从 start 开始遍历到 N - 1,这样就可以枚举出所有的子数组区间 [start, end]
    • 对于每一个确定的子数组区间 [start, end],进行翻转操作。可以创建一个新的临时数组(长度与原数组相同),先将原数组中不在该子数组区间的元素按顺序复制到临时数组中,然后将子数组区间内的元素逆序复制到临时数组对应的位置,这样就得到了翻转后的数组副本。例如原数组 [1, 2, 3, -1, 4],若要翻转子数组 [2, 3],则临时数组先复制 [1],再逆序复制 [3, 2],最后复制 [-1, 4],得到 [1, 3, 2, -1, 4]
    • 接着,同样使用前面计算原始数组最大子数组和的动态规划方法,计算这个翻转后数组的最大子数组和,记为 flipped_max
    • 每得到一个 flipped_max,就与之前记录的最大结果进行比较,更新全局最大的子数组和记录,即 max_result = max(max_result, flipped_max)。这里的 max_result 初始值可以设为 original_max,因为它是不翻转情况下的最大子数组和,是最初的一个比较基准。
  3. 最终结果输出
    经过上述步骤,不断更新 max_result,遍历完所有可能的子数组翻转情况后,max_result 就是整个问题的答案,也就是执行翻转操作后(包含不翻转情况)数组中可能获得的最大子数组和,直接将其返回即可。

下面我们就可以开始写AC代码了:

def max_subarray_sum(arr):
    """计算数组的最大子数组和"""
    max_sum = float('-inf')
    curr_sum = 0
    for num in arr:
        curr_sum = max(num, curr_sum + num)
        max_sum = max(max_sum, curr_sum)
    return max_sum

def solution(N, data_array):
    # 计算不翻转时的最大子数组和
    max_possible_sum = max_subarray_sum(data_array)
    
    # 尝试所有可能的子数组翻转
    for i in range(N):
        for j in range(i + 1, N):
            # 翻转子数组
            flipped = data_array[:i] + data_array[i:j+1][::-1] + data_array[j+1:]
            # 计算翻转后的最大子数组和
            curr_max = max_subarray_sum(flipped)
            max_possible_sum = max(max_possible_sum, curr_max)
    
    return max_possible_sum

if __name__ == "__main__":
    #  测试用例
    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)