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

59 阅读3分钟

问题描述

小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。

备注:子数组 是数组中的一个连续部分。

注意到这道题由于可以将数列左右翻转,通过一次翻转操作等价于我们可以连接两个没有交集的连续子序列。 具体如下: 假设原数列被划分为多个连续子序列A1,A2,...,AnA1,A2,...,An 我们想要连接An,AmAn,Am,(不妨令n<m)那么我们可以通过翻转An+1,...,AmA_{n+1},...,A_{m}组成的序列,做到An,AmAn,Am直接相连,从而实现目标。

因此这道题本质上是求原序列最大的两个不相交连续子序列的和。

Step1:解决最大不相交 采用分治的思想,枚举中点,将序列分为两部分(1-p,p+1-n),分别求两部分的最大连续子序列。

Step2:求两部分的最大连续子序列。

这里采用前/后缀和的思想完成。 以1-p的前缀和为例。 一段连续的子序列i=nmai\sum_{i = n}^{m}{a_{i}} 的和等价于前缀和summsumn1sum_{m} - sum_{n - 1}。 所以我们可以从前往后,保存当前最小的前缀和作为左端点,用当前的前缀和减去当前最小前缀就能得到当前可能最大的连续子段和,记录为maximax_{i}。 同时与上一位的maxi1max_{i-1}取最大,就得到了当前最大的连续子段和。

至此问题得到解决。 我们先执行Step2再执行Step1,得到答案。

以下是python的代码

def solution(N, data_array):
    # 创建必要的数组
    a = [0] * (N + 1)
    s1 = [0] * (N + 2) 
    s2 = [0] * (N + 1)
    l = [0] * (N + 2) 
    r = [0] * (N + 1)
    ans = -2147483647

    # 填充数组
    for i in range(1, N + 1):
        a[i] = data_array[i - 1]

    mn = 0
    for i in range(N, 0, -1):
        s1[i] = s1[i + 1] + a[i]
        l[i] = s1[i] - mn
        mn = min(mn, s1[i])
        
    for i in range(N - 1, 0, -1):
        l[i] = max(l[i], l[i + 1])

    mn = 0
    for i in range(1, N + 1):
        s2[i] = s2[i - 1] + a[i]
        
        r[i] = s2[i] - mn
        mn = min(mn, s2[i])
    for i in range(2, N + 1):
        r[i] = max(r[i], r[i - 1])

    for i in range(1, N + 1):
        ans = max(ans, r[i - 1] + l[i])

    return ans

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)