最大子数组和问题(允许最多翻转一个子数组)
字数 5896 2025-11-28 07:21:13

最大子数组和问题(允许最多翻转一个子数组)

题目描述
给定一个整数数组,你可以选择翻转数组中的任意一个连续子数组(将子数组的元素顺序完全反转),然后返回可能的最大子数组和。注意,你最多只能进行一次翻转操作,也可以选择不进行任何翻转。

例如:
输入:[1, -2, 3, -4, 5]
输出:11
解释:翻转子数组[-2, 3, -4]得到[1, 3, -2, -4, 5],最大子数组和为3 + (-2) + (-4) + 5 = 11?实际上,更优解是翻转[-2, 3, -4]后数组变为[1, 3, -2, 5](注意原数组是[1, -2, 3, -4, 5],翻转下标1到3的子数组[-2, 3, -4]得到[1, -4, 3, -2, 5],但最大子数组和是3 + (-2) + 5 = 6?让我们重新计算:翻转后数组为[1, -4, 3, -2, 5],最大子数组是[3, -2, 5]和为6。但答案是11?显然题目示例有误。实际上,正确示例应该是:
输入:[1, -2, 3, -4, 5]
翻转子数组[1, -2, 3, -4]?不,让我们重新理解:实际上,翻转一个子数组相当于将原数组分为三部分A、B、C,翻转B后,最大子数组和可能出现在A、C中,或者跨越A和翻转后的B,或者跨越翻转后的B和C,或者完全在翻转后的B中。但典型解法是计算不翻转的最大子数组和,以及翻转某个子数组后能得到的最大子数组和。经典解法是使用动态规划预处理前后缀信息。

解题过程

步骤1:理解问题
我们需要找到最大子数组和,但允许翻转任意一个子数组一次。翻转操作可以视为将原数组的某个子数组反转,这可能会将负的子数组反转后变成正的贡献,或者将正的子数组反转后连接其他正数形成更大的和。

步骤2:分析翻转的影响
设原数组为arr,长度为n。翻转一个子数组arr[i:j](0-indexed,左闭右开)后,数组变为:
arr[0:i] + reverse(arr[i:j]) + arr[j:n]
最大子数组和可能出现在:

  1. 不翻转:直接求原数组的最大子数组和(经典Kadane算法结果)。
  2. 翻转后:最大子数组可能跨越翻转子数组的边界,即利用翻转后的新连接。

关键观察:翻转arr[i:j]等价于将原数组分为三部分:左部分L=arr[0:i],中间部分M=arr[i:j],右部分R=arr[j:n]。翻转M后,最大子数组和可能是:

  • 左部分的最大后缀和(L的最大后缀和) + 翻转后M的最大前缀和(即原M的最大后缀和,因为翻转后M的前缀就是原M的后缀) + 右部分的最大前缀和(R的最大前缀和)。
    因为翻转M后,M部分本身的最大子数组和不变(反转不改变子数组和,只改变顺序,但最大子数组和可能由M中一段连续元素组成,反转后这段连续元素仍然存在,只是顺序反了,和不变)。所以,翻转操作的主要收益是可能将L的后段、M的全部、R的前段连接成一个更大的连续子数组。

更精确的解法:问题转化为求max(不翻转的最大子数组和, 所有翻转操作中能得到的最大子数组和)。而翻转操作能得到的最大子数组和 = max_{0<=i<=j<=n} [左部分的最大后缀和(0..i-1) + 中间部分的总和(i..j-1) + 右部分的最大前缀和(j..n-1)]?不对,因为中间部分的总和不一定全部取,我们可能只取中间部分的一段。实际上,翻转arr[i:j]后,最大子数组和可以是:
左部分的最大后缀和 + 中间部分的最大子数组和(翻转后不变) + 右部分的最大前缀和,但中间部分的最大子数组和可能不是整个M,而是M的一段。但注意,翻转后M段内最大子数组和不变,所以翻转的收益主要体现在连接L和R上:即翻转后,我们可以取L的最大后缀和(不取整个L,只取末尾一段)加上整个M(因为翻转后M的顺序变了,但我们可以取整个M,其总和不变)加上R的最大前缀和(只取开头一段)。这样,翻转arr[i:j]能得到的最大子数组和就是:maxSuffixLeft[i] + sum(M) + maxPrefixRight[j],其中maxSuffixLeft[i]表示原数组从0到i-1的最大后缀和(必须以i-1结尾),sum(M)是arr[i]到arr[j-1]的总和,maxPrefixRight[j]表示原数组从j到n-1的最大前缀和(必须以j开头)。

但这样可能重复计算?实际上,我们要求的是连续子数组,翻转后L的后缀、整个M、R的前缀是连续的。所以最大子数组和可能是这三部分的和。但注意,我们不一定取整个M,但取整个M通常是最优的,因为如果M中有正有负,我们可能不想取整个M,但我们可以通过预处理M的最大子数组和来考虑?不对,因为翻转后M段内最大子数组和不变,所以翻转的主要好处是连接L和R,而不是改变M内部。因此,标准解法是:翻转arr[i:j]能得到的最大子数组和 = maxSuffixLeft[i] + sum(M) + maxPrefixRight[j],其中sum(M)是M的总和。这样,我们假设取了整个M,因为如果不想取整个M,那么翻转操作可能无益(不如不翻转)。

步骤3:动态规划预处理
我们需要预处理以下数组:

  • maxEndingHere[i]:以i结尾的最大子数组和(经典Kadane)。
  • maxStartingHere[i]:以i开头的最大子数组和。
  • maxPrefixSum[i]:从0到i的最大前缀和(不一定以i结尾,是0..i的最大子数组和)。
  • maxSuffixSum[i]:从i到n-1的最大后缀和(不一定以i开头,是i..n-1的最大子数组和)。
    但更精确地,我们需要:
  • leftMax[i]:表示从0到i-1这部分的最大后缀和(必须以i-1结尾),即从某个起点到i-1的最大和。
  • rightMax[j]:表示从j到n-1这部分的最大前缀和(必须以j开头),即从j到某个终点的最大和。
  • 另外,需要前缀和数组prefixSum,以便快速计算任意子数组和。

实际上,标准解法:

  1. 计算不翻转的最大子数组和res = Kadane(arr)。

  2. 预处理leftMax数组:leftMax[i]表示在i之前(不含i)的最大后缀和(即以i-1结尾的最大子数组和)。注意,leftMax[0] = 0(因为i=0时左边无元素)。
    计算方式:遍历i从0到n,维护当前后缀和cur,如果cur<0则重置为0,leftMax[i] = max(leftMax[i-1], cur)?不对,leftMax[i]应该是以i-1结尾的最大后缀和。我们可以用动态规划:
    设dp[i]表示以i结尾的最大子数组和,则leftMax[i] = max(leftMax[i-1], dp[i-1])?但leftMax[i]应该只记录到i-1为止的最大后缀和。更简单:初始化leftMax[0]=0,然后遍历i从1到n:
    curSuffix = max(0, curSuffix + arr[i-1]) # 注意arr索引从0开始,i是左部分结束索引(不含i)
    leftMax[i] = max(leftMax[i-1], curSuffix)
    但这样leftMax[i]是0..i-1的最大后缀和(可能不取到i-1,但实际应取到i-1?)不对,最大后缀和必须以i-1结尾。所以正确做法:定义leftMax[i]为以i-1结尾的最大后缀和(即必须包含arr[i-1])。则leftMax[i] = max(0, leftMax[i-1] + arr[i-1])?但leftMax[i]应该独立于i-1的值。我们重新定义:
    设L[i] = 以i结尾的最大子数组和(经典Kadane),则对于位置i,左边部分的最大后缀和就是L[i-1](如果i>0)。但我们需要对于每个分界点i,左边部分的最大后缀和。所以预处理数组leftMax,其中leftMax[i] = 从0到i-1的最大后缀和(必须以i-1结尾),即leftMax[i] = L[i-1](当i>0),leftMax[0]=0。
    类似地,预处理rightMax[j] = 从j到n-1的最大前缀和(必须以j开头),即rightMax[j] = R[j],其中R[j]是以j开头的最大子数组和。

  3. 预处理前缀和数组pref,pref[i] = arr[0]+...+arr[i-1],则子数组arr[i:j]的和 = pref[j] - pref[i]。

  4. 枚举所有可能的翻转区间[i,j](0<=i<j<=n),计算翻转后的候选值:leftMax[i] + (pref[j]-pref[i]) + rightMax[j]。这里假设我们取整个翻转区间和加上左最大后缀和右最大前缀。

  5. 最终答案 = max(不翻转的最大子数组和, 所有翻转候选值中的最大值)。

步骤4:示例验证
取数组[1, -2, 3, -4, 5]。
不翻转:Kadane算法得最大子数组和=5(子数组[5])或[3]为3,实际最大是[3,-4,5]? 和=4,还是[5]和=5?计算:从头开始,1和=1,1-2=-1,3和=3,3-4=-1,5和=5。最大是5。
预处理:
leftMax:
i=0: leftMax[0]=0
i=1: 以0结尾的最大子数组和=L[0]=1, leftMax[1]=1
i=2: 以1结尾的最大子数组和=max(-2, 1-2)= -1? 但最大后缀和需非负?不,可以为负,但如果我们允许不取,则取0。但这里leftMax应允许负?不,因为如果负,我们不如不取,所以leftMax[i]应该取max(0, 以i-1结尾的最大后缀和)。所以leftMax[0]=0, i=1: 以0结尾最大和=1,取max(0,1)=1。i=2: 以1结尾最大和=max(arr[1], arr[1]+以0结尾最大和)=max(-2, -2+1)=max(-2,-1)=-1,但取0(因为负则舍去)。所以leftMax[2]=0? 但这样可能丢失信息。实际上,标准解法中leftMax应允许负,因为后面加上的区间和可能为负,但整体和可能正。但通常我们取max(0, value)来避免负值拖累,因为负的后缀不如不取。所以leftMax[i] = max(0, 以i-1结尾的最大后缀和)。

类似rightMax[j] = max(0, 以j开头的最大前缀和)。

计算前缀和pref:
pref[0]=0, pref[1]=1, pref[2]=-1, pref[3]=2, pref[4]=-2, pref[5]=3.

枚举翻转区间:
例如翻转i=1,j=4(子数组[-2,3,-4]),候选值= leftMax[1] + (pref[4]-pref[1]) + rightMax[4] = 1 + (-2 - 1) + max(0,以4开头的最大前缀和)。以4开头最大前缀和=5,所以rightMax[4]=5。计算:1 + (-3) + 5 = 3。小于不翻转的5。
翻转i=1,j=3(子数组[-2,3]),候选值=1 + (pref[3]-pref[1]) + rightMax[3] = 1 + (2-1) + max(0,以3开头的最大前缀和)。以3开头最大前缀和= max(3, 3-4, 3-4+5)=max(3,-1,4)=4,所以1+1+4=6。
翻转i=2,j=4(子数组[3,-4]),候选值= leftMax[2] + (pref[4]-pref[2]) + rightMax[4] = 0 + (-2 - (-1)) + 5 = 0 -1 +5=4。
翻转i=1,j=5(整个数组除两头?),候选值=1 + (pref[5]-pref[1]) + rightMax[5]=1+(3-1)+0=3。
最大候选值是6(翻转[-2,3]),最终答案=max(5,6)=6。但示例输出是11?说明示例有误。实际上,正确输出应为6。

步骤5:算法总结

  1. 计算不翻转的最大子数组和res = Kadane(arr)。
  2. 预处理leftMax数组:长度n+1,leftMax[0]=0,cur=0,for i from 1 to n: cur = max(0, cur + arr[i-1]); leftMax[i] = max(leftMax[i-1], cur)? 不对,leftMax[i]应表示以i-1结尾的最大后缀和(且非负,即如果负则取0)。所以更简单:leftMax[i] = max(0, 以i-1结尾的最大后缀和)。而以i-1结尾的最大后缀和可以用动态规划:设dp[i]表示以i结尾的最大子数组和,则dp[0]=arr[0],dp[i]=max(arr[i], dp[i-1]+arr[i])。然后leftMax[i] = max(0, dp[i-1]) for i>=1, leftMax[0]=0。
  3. 类似预处理rightMax数组:长度n+1,rightMax[n]=0,从右向左计算dp[i]表示以i开头的最大子数组和,则rightMax[i] = max(0, dp[i]) for i<n。
  4. 计算前缀和pref。
  5. 枚举所有i<j,计算候选值 = leftMax[i] + (pref[j]-pref[i]) + rightMax[j],更新最大候选值。
  6. 答案 = max(res, 最大候选值)。

时间复杂度O(n^2),可优化?实际上,枚举i和j是O(n^2),对于大n可能慢,但此题n通常较小(<=1000)。如果n大,可以用更优方法?但题目一般n不超过1000。

步骤6:边界情况

  • 数组全正:翻转无益,答案等于总和。
  • 数组全负:翻转无益,答案等于最大元素(负数中最大的)。
  • 数组长度1:翻转无意义,答案等于该元素。

通过以上步骤,我们解决了允许翻转一个子数组的最大子数组和问题。

最大子数组和问题(允许最多翻转一个子数组) 题目描述 给定一个整数数组,你可以选择翻转数组中的任意一个连续子数组(将子数组的元素顺序完全反转),然后返回可能的最大子数组和。注意,你最多只能进行一次翻转操作,也可以选择不进行任何翻转。 例如: 输入:[ 1, -2, 3, -4, 5 ] 输出:11 解释:翻转子数组[ -2, 3, -4]得到[ 1, 3, -2, -4, 5],最大子数组和为3 + (-2) + (-4) + 5 = 11?实际上,更优解是翻转[ -2, 3, -4]后数组变为[ 1, 3, -2, 5](注意原数组是[ 1, -2, 3, -4, 5],翻转下标1到3的子数组[ -2, 3, -4]得到[ 1, -4, 3, -2, 5],但最大子数组和是3 + (-2) + 5 = 6?让我们重新计算:翻转后数组为[ 1, -4, 3, -2, 5],最大子数组是[ 3, -2, 5 ]和为6。但答案是11?显然题目示例有误。实际上,正确示例应该是: 输入:[ 1, -2, 3, -4, 5 ] 翻转子数组[ 1, -2, 3, -4 ]?不,让我们重新理解:实际上,翻转一个子数组相当于将原数组分为三部分A、B、C,翻转B后,最大子数组和可能出现在A、C中,或者跨越A和翻转后的B,或者跨越翻转后的B和C,或者完全在翻转后的B中。但典型解法是计算不翻转的最大子数组和,以及翻转某个子数组后能得到的最大子数组和。经典解法是使用动态规划预处理前后缀信息。 解题过程 步骤1:理解问题 我们需要找到最大子数组和,但允许翻转任意一个子数组一次。翻转操作可以视为将原数组的某个子数组反转,这可能会将负的子数组反转后变成正的贡献,或者将正的子数组反转后连接其他正数形成更大的和。 步骤2:分析翻转的影响 设原数组为arr,长度为n。翻转一个子数组arr[ i:j ](0-indexed,左闭右开)后,数组变为: arr[ 0:i] + reverse(arr[ i:j]) + arr[ j:n ] 最大子数组和可能出现在: 不翻转:直接求原数组的最大子数组和(经典Kadane算法结果)。 翻转后:最大子数组可能跨越翻转子数组的边界,即利用翻转后的新连接。 关键观察:翻转arr[ i:j]等价于将原数组分为三部分:左部分L=arr[ 0:i],中间部分M=arr[ i:j],右部分R=arr[ j:n ]。翻转M后,最大子数组和可能是: 左部分的最大后缀和(L的最大后缀和) + 翻转后M的最大前缀和(即原M的最大后缀和,因为翻转后M的前缀就是原M的后缀) + 右部分的最大前缀和(R的最大前缀和)。 因为翻转M后,M部分本身的最大子数组和不变(反转不改变子数组和,只改变顺序,但最大子数组和可能由M中一段连续元素组成,反转后这段连续元素仍然存在,只是顺序反了,和不变)。所以,翻转操作的主要收益是可能将L的后段、M的全部、R的前段连接成一个更大的连续子数组。 更精确的解法:问题转化为求max(不翻转的最大子数组和, 所有翻转操作中能得到的最大子数组和)。而翻转操作能得到的最大子数组和 = max_ {0<=i<=j<=n} [ 左部分的最大后缀和(0..i-1) + 中间部分的总和(i..j-1) + 右部分的最大前缀和(j..n-1)]?不对,因为中间部分的总和不一定全部取,我们可能只取中间部分的一段。实际上,翻转arr[ i:j ]后,最大子数组和可以是: 左部分的最大后缀和 + 中间部分的最大子数组和(翻转后不变) + 右部分的最大前缀和,但中间部分的最大子数组和可能不是整个M,而是M的一段。但注意,翻转后M段内最大子数组和不变,所以翻转的收益主要体现在连接L和R上:即翻转后,我们可以取L的最大后缀和(不取整个L,只取末尾一段)加上整个M(因为翻转后M的顺序变了,但我们可以取整个M,其总和不变)加上R的最大前缀和(只取开头一段)。这样,翻转arr[ i:j]能得到的最大子数组和就是:maxSuffixLeft[ i] + sum(M) + maxPrefixRight[ j],其中maxSuffixLeft[ i]表示原数组从0到i-1的最大后缀和(必须以i-1结尾),sum(M)是arr[ i]到arr[ j-1]的总和,maxPrefixRight[ j ]表示原数组从j到n-1的最大前缀和(必须以j开头)。 但这样可能重复计算?实际上,我们要求的是连续子数组,翻转后L的后缀、整个M、R的前缀是连续的。所以最大子数组和可能是这三部分的和。但注意,我们不一定取整个M,但取整个M通常是最优的,因为如果M中有正有负,我们可能不想取整个M,但我们可以通过预处理M的最大子数组和来考虑?不对,因为翻转后M段内最大子数组和不变,所以翻转的主要好处是连接L和R,而不是改变M内部。因此,标准解法是:翻转arr[ i:j]能得到的最大子数组和 = maxSuffixLeft[ i] + sum(M) + maxPrefixRight[ j ],其中sum(M)是M的总和。这样,我们假设取了整个M,因为如果不想取整个M,那么翻转操作可能无益(不如不翻转)。 步骤3:动态规划预处理 我们需要预处理以下数组: maxEndingHere[ i ]:以i结尾的最大子数组和(经典Kadane)。 maxStartingHere[ i ]:以i开头的最大子数组和。 maxPrefixSum[ i ]:从0到i的最大前缀和(不一定以i结尾,是0..i的最大子数组和)。 maxSuffixSum[ i ]:从i到n-1的最大后缀和(不一定以i开头,是i..n-1的最大子数组和)。 但更精确地,我们需要: leftMax[ i ]:表示从0到i-1这部分的最大后缀和(必须以i-1结尾),即从某个起点到i-1的最大和。 rightMax[ j ]:表示从j到n-1这部分的最大前缀和(必须以j开头),即从j到某个终点的最大和。 另外,需要前缀和数组prefixSum,以便快速计算任意子数组和。 实际上,标准解法: 计算不翻转的最大子数组和res = Kadane(arr)。 预处理leftMax数组:leftMax[ i]表示在i之前(不含i)的最大后缀和(即以i-1结尾的最大子数组和)。注意,leftMax[ 0 ] = 0(因为i=0时左边无元素)。 计算方式:遍历i从0到n,维护当前后缀和cur,如果cur<0则重置为0,leftMax[ i] = max(leftMax[ i-1], cur)?不对,leftMax[ i ]应该是以i-1结尾的最大后缀和。我们可以用动态规划: 设dp[ i]表示以i结尾的最大子数组和,则leftMax[ i] = max(leftMax[ i-1], dp[ i-1])?但leftMax[ i]应该只记录到i-1为止的最大后缀和。更简单:初始化leftMax[ 0 ]=0,然后遍历i从1到n: curSuffix = max(0, curSuffix + arr[ i-1 ]) # 注意arr索引从0开始,i是左部分结束索引(不含i) leftMax[ i] = max(leftMax[ i-1 ], curSuffix) 但这样leftMax[ i]是0..i-1的最大后缀和(可能不取到i-1,但实际应取到i-1?)不对,最大后缀和必须以i-1结尾。所以正确做法:定义leftMax[ i]为以i-1结尾的最大后缀和(即必须包含arr[ i-1])。则leftMax[ i] = max(0, leftMax[ i-1] + arr[ i-1])?但leftMax[ i ]应该独立于i-1的值。我们重新定义: 设L[ i] = 以i结尾的最大子数组和(经典Kadane),则对于位置i,左边部分的最大后缀和就是L[ i-1](如果i>0)。但我们需要对于每个分界点i,左边部分的最大后缀和。所以预处理数组leftMax,其中leftMax[ i] = 从0到i-1的最大后缀和(必须以i-1结尾),即leftMax[ i] = L[ i-1](当i>0),leftMax[ 0 ]=0。 类似地,预处理rightMax[ j] = 从j到n-1的最大前缀和(必须以j开头),即rightMax[ j] = R[ j],其中R[ j ]是以j开头的最大子数组和。 预处理前缀和数组pref,pref[ i] = arr[ 0]+...+arr[ i-1],则子数组arr[ i:j]的和 = pref[ j] - pref[ i ]。 枚举所有可能的翻转区间[ i,j](0<=i<j<=n),计算翻转后的候选值:leftMax[ i] + (pref[ j]-pref[ i]) + rightMax[ j ]。这里假设我们取整个翻转区间和加上左最大后缀和右最大前缀。 最终答案 = max(不翻转的最大子数组和, 所有翻转候选值中的最大值)。 步骤4:示例验证 取数组[ 1, -2, 3, -4, 5 ]。 不翻转:Kadane算法得最大子数组和=5(子数组[ 5])或[ 3]为3,实际最大是[ 3,-4,5]? 和=4,还是[ 5 ]和=5?计算:从头开始,1和=1,1-2=-1,3和=3,3-4=-1,5和=5。最大是5。 预处理: leftMax: i=0: leftMax[ 0 ]=0 i=1: 以0结尾的最大子数组和=L[ 0]=1, leftMax[ 1 ]=1 i=2: 以1结尾的最大子数组和=max(-2, 1-2)= -1? 但最大后缀和需非负?不,可以为负,但如果我们允许不取,则取0。但这里leftMax应允许负?不,因为如果负,我们不如不取,所以leftMax[ i]应该取max(0, 以i-1结尾的最大后缀和)。所以leftMax[ 0]=0, i=1: 以0结尾最大和=1,取max(0,1)=1。i=2: 以1结尾最大和=max(arr[ 1], arr[ 1]+以0结尾最大和)=max(-2, -2+1)=max(-2,-1)=-1,但取0(因为负则舍去)。所以leftMax[ 2]=0? 但这样可能丢失信息。实际上,标准解法中leftMax应允许负,因为后面加上的区间和可能为负,但整体和可能正。但通常我们取max(0, value)来避免负值拖累,因为负的后缀不如不取。所以leftMax[ i ] = max(0, 以i-1结尾的最大后缀和)。 类似rightMax[ j ] = max(0, 以j开头的最大前缀和)。 计算前缀和pref: pref[ 0]=0, pref[ 1]=1, pref[ 2]=-1, pref[ 3]=2, pref[ 4]=-2, pref[ 5 ]=3. 枚举翻转区间: 例如翻转i=1,j=4(子数组[ -2,3,-4]),候选值= leftMax[ 1] + (pref[ 4]-pref[ 1]) + rightMax[ 4] = 1 + (-2 - 1) + max(0,以4开头的最大前缀和)。以4开头最大前缀和=5,所以rightMax[ 4 ]=5。计算:1 + (-3) + 5 = 3。小于不翻转的5。 翻转i=1,j=3(子数组[ -2,3]),候选值=1 + (pref[ 3]-pref[ 1]) + rightMax[ 3 ] = 1 + (2-1) + max(0,以3开头的最大前缀和)。以3开头最大前缀和= max(3, 3-4, 3-4+5)=max(3,-1,4)=4,所以1+1+4=6。 翻转i=2,j=4(子数组[ 3,-4]),候选值= leftMax[ 2] + (pref[ 4]-pref[ 2]) + rightMax[ 4 ] = 0 + (-2 - (-1)) + 5 = 0 -1 +5=4。 翻转i=1,j=5(整个数组除两头?),候选值=1 + (pref[ 5]-pref[ 1]) + rightMax[ 5 ]=1+(3-1)+0=3。 最大候选值是6(翻转[ -2,3 ]),最终答案=max(5,6)=6。但示例输出是11?说明示例有误。实际上,正确输出应为6。 步骤5:算法总结 计算不翻转的最大子数组和res = Kadane(arr)。 预处理leftMax数组:长度n+1,leftMax[ 0]=0,cur=0,for i from 1 to n: cur = max(0, cur + arr[ i-1]); leftMax[ i] = max(leftMax[ i-1], cur)? 不对,leftMax[ i]应表示以i-1结尾的最大后缀和(且非负,即如果负则取0)。所以更简单:leftMax[ i] = max(0, 以i-1结尾的最大后缀和)。而以i-1结尾的最大后缀和可以用动态规划:设dp[ i]表示以i结尾的最大子数组和,则dp[ 0]=arr[ 0],dp[ i]=max(arr[ i], dp[ i-1]+arr[ i])。然后leftMax[ i] = max(0, dp[ i-1]) for i>=1, leftMax[ 0 ]=0。 类似预处理rightMax数组:长度n+1,rightMax[ n]=0,从右向左计算dp[ i]表示以i开头的最大子数组和,则rightMax[ i] = max(0, dp[ i]) for i <n。 计算前缀和pref。 枚举所有i<j,计算候选值 = leftMax[ i] + (pref[ j]-pref[ i]) + rightMax[ j ],更新最大候选值。 答案 = max(res, 最大候选值)。 时间复杂度O(n^2),可优化?实际上,枚举i和j是O(n^2),对于大n可能慢,但此题n通常较小( <=1000)。如果n大,可以用更优方法?但题目一般n不超过1000。 步骤6:边界情况 数组全正:翻转无益,答案等于总和。 数组全负:翻转无益,答案等于最大元素(负数中最大的)。 数组长度1:翻转无意义,答案等于该元素。 通过以上步骤,我们解决了允许翻转一个子数组的最大子数组和问题。