动态规划求解最大子段和(两种写法+还原最优解)MarisaMagic

(本文作者:Amαdeus,未经允许不得转载哦。)

给定一个长度为len的序列:a[0],a[1],a[2]...a[len-1],求在这个序列中一个子区间[j,i]使得a[j]+a[j+1]+...+a[i]的和最大,并求出这个最大累加和。比如一个序列为{-6,7,-1,5,11,-7,2},那么其最大子段和结果为22,所求子区间范围是[1,4]。

最优子结构:原问题的最优解包含了子问题的最优解。

假设求得了某两个最大子段和区间,分别为[j,i]和[j,i-1],前一个子区间的元素分别为{a[j],a[j+1]...a[i]},后一个子区间的元素分别为{a[j],a[j+1]...a[i-1]}。我们很容易发现,后一个子区间[j,i-1]同时也是前一个子区间[j,i]的子区间。

假设我们的两个子区间范围内的最大累加和分别是maxA和maxB,那么可以得出maxA必然包含了maxB。也就是说maxA=maxB+a[i]or0,当a[i]为正数时,我们可以加上a[i],这样[j,i]的最大累加和相较于[j,i-1]就更大;当a[i]为负数时,我们加上它之后只会减小区间[j,i]的最大累加和,相较于[j,i-1]反而更小,所以此时不加上a[i]。

由此可见,最大子段和问题是满足最优子结构性质的。

对于最大子段和问题,我们都是在先前的状态基础上更新当前的最优解。假设求得之前某个最大子段和区间[j,i-1],那么我们现在要求区间[j,i]的最大累加和,那么我们只需要在之前区间[j,i-1]确定状态的基础上,更新当前区间[j,i]的最优解,这个问题就是在上一个小标题里我说到的是否加上a[i]。

由此可见,最大子段和是满足无后效性的。

重叠子问题:在过程中重复计算相同的子问题,即子问题之间不是相互独立的,子问题的最优解在之后的决策中会被用到。在上面的阐述中也已经提到,假设我们求得某两个最大子段和子区间分别是[j,i]和[j,i-1],区间[j,i-1]的最大累加和maxB是先前已经确定的状态,我们求解[j,i]的最大累加和maxA需要用到这个maxB,无需再次计算maxB。

很显然,最大子段和问题也是满足重叠子问题性质的。

综上,我们可以用动态规划算法来求解最大子段和(最大子数组累加和)问题。

1、首先我们需要定义dp[]数组来记录每个状态下的最优解;

2、为了还原最优解,我们需要定义rec[]数组来记录边界下标;

3、构建递推方程,求出最大子段和,并还原具体最优解。

在求解这个问题时,我想到了两种求解最大子段和问题的思路。一种是从前往后记录的方法,一种从后往前记录的方法(这些名字都是我自己取的)。下面我会分别介绍这两种思路,并且给出各自的递推方程。

我们的dp[]数组,在这个从前往后记录的方法中,记录的是以下标为i为结尾下标的子区间的最大累加和,与此同时rec[]数组记录的是对应以下标为i为结尾下标的子区间的左边界(这些很重要)。也就是说,如果我们的最大子段和最终答案是dp[i],那么对应的区间是[rec[i],i],我们也可以定义left=rec[i],right=i,这样更清晰一点。初始情况下,dp[0]=a[0],rec[0]=0。

我们以{4,-5,8,9,-19,3,6,-1,10,-2}为例

说实话这个思路略有一点逆思维,其实只是扫描方向的区别,就好像是把数组翻转一下,事实证明从后往前记录的顺序也是同样可以解决最大子段和问题的,只不过我在网上很少看到有人写,所以决定自己来写一下。

不同的是我们的dp[]数组,在这个从后往前记录的方法中,记录的是以下标为i为起始下标的子区间的最大累加和,与此同时rec[]数组记录的是对应以下标为i为起始下标的子区间的右边界(这些还是很重要)。也就是说,如果我们的最大子段和最终答案是dp[i],那么对应的区间是[i,rec[i]],我们也可以最后定义left=i,right=rec[i]。初始情况下,若数组最后一个下标为n,那么dp[n]=a[n],rec[n]=n。(注意和之前方法的不同哟~)

在递推过程中,我们发现每一次更新当前区间范围最优解dp[i],只用到了上一层的dp[i-1](或者dp[i+1])。那么我们是否可以不用dp[]数组呢?答案是可以的。我们可以定义一个变量sum,来一直记录当前子区间范围的最优解。这样可以省去dp[]数组,虽然我自己写代码时使用的动态分配存储空间的数组,后期能够回收内存空间。主要是对于不太会用这个技巧的童鞋,省去dp[]数组,可以节省一定的存储空间。

同时为了还原具体最优解,rec[]也不能闲着,还是需要记录其边界。若为从前往后记录,那么rec[i]在sum>0时就等于rec[i-1];在sum<=0时就等于i。并且还需要一个right不断记录右边界。若为从后往前记录,那么rec[i]在sum>0时就等于rec[i+1];在sum<=0时就等于i。并且还需要一个left不断记录左边界。

THE END
1.动态规划———最大子段和最大子段和问题是将一个n个整数的序列a[1],a[2]….a[n]中字段a[first]….a[last]之和,(1<=first<=last<=n)求这些子段和中最大的。 例如(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20,子段为a[2],a[3],a[4]。 https://blog.csdn.net/qq_50675813/article/details/115498809
2.最大子段和问题的四种算法(暴力法优化后的暴力法分治算法当所给的整均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])}=(-2,11,-4,13,-5,-2)时,最大子段和为20。 最大子段和是动态规划中的一种。https://www.jianshu.com/p/e79fa0067203
3.算法设计与分析(lefts > s1) s1 = lefts; //左边最大值放在s1 } int s2 = 0; int rights = 0; for(int j = center + 1; j <= right; j++)//求解s2 { rights += a[j]; if(rights > s2) s2 =rights; } sum = s1 + s2; //计算第3钟情况的最大子段和 if(sum < leftsum) sum = leftsum;https://www.iteye.com/blog/bcyy-1875625
4.算法与数据结构4.7最大子段和动态规划51CTO博客博客对应课程的视频位置:4.7、最大子段和-动态规划 1、题目描述 最大子段和(最大连续子序列的和) 题目描述 给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。 输入格式 第一行是一个整数,表示序列的长度 n。 https://blog.51cto.com/u_15069487/4320429
5.最大m子段和问题(动态规划(又来填表了))如给定一个数组{1,-2,3,4,-5,-6}和一个正整数m=2,明显当两个子段分别为{1}和{3,4}时,得到最大m子段和,最大m子段和为8。 2.思路 可以利用动态规划的思想解决该问题。 定义二维数组dp,令dpi表示前 j 项所构成 i 子段的最大和,且必须包含着第j项,即以第j项结尾。(这个定义很重要,一定要理https://cloud.tencent.com/developer/article/1629359
6.算法设计与分析课程中最大子段和问题的教学探讨【摘要】:介绍算法设计与分析课程中最大子段和问题的动态规划解法,其求解思想是先求给定序列中以每一个元素为尾元素的最大子段和,然后其中的最大者便是整个序列的最大子段和。从两个不同的角度分析最大子段和问题最优解的构造方法,给出最大子段和问题的动态规划算法,并分析算法的时间复杂度。通过这一问题的https://www.cnki.com.cn/Article/CJFDTotal-ZJJB201327020.htm
7.区间最大子段和(线段树)最大子段和-动态规划算法 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。 要求算法的时间复杂度为O(n)。 输入格式: 输入有两行: 第一行是n值(1<=n<=10000); 第二行是n个整https://www.pianshen.com/article/3587416326/
8.顺序表应用8:最大子段和之动态规划法SDUTOnlineJudge顺序表应用8:最大子段和之动态规划法 Description 给定n(1<=n<=100000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},https://acm.sdut.edu.cn/onlinejudge3/problems/3665