最大子段和及其升级版

最大子段和及其升级版

最大子段和又是一个常见而且经典的模型.像前面的背包一样,由它又可以扩展出很多类似的模型.

1.基础:(一维)最大子段和
例子:HOJ 1760 The Jackpot

这就是最基础的最大子段和模型:给出一个序列a[0],a[1],a[2],...a[n],要求出连续的一段,使其总和最大.如果设dp[i]表示以第i个元素为结尾的最大总和,那么显然有:
dp[i] = dp[i - 1] + a[i] (dp[i - 1] > 0 时)
a[i] (dp[i - 1] < 0 时)
显然这个模型空间复杂度可以压缩至O(1),即:
dp += a[i] (dp > 0)
dp = a[i] (dp < 0)

2.变形1:最大子矩阵和
例子:HOJ 2558 maxsum

所谓万变不离其宗,这个的思路和上面是一样的. 维数增加了一维,所以可以考虑把它转化成一维的"基本问题". 我们可以先统计sum[i][j](以下假设下标从1开始) : 第i行,从开头到第j个元素的总值,这样, 第i行从第j个元素到第k个元素的总价值就是sum[i][k] - sum[i][j - 1]. 这个预处理的时间复杂度是O(N^2). 这时,这个问题就转化成了一维的最大子段和问题了: 枚举每一行中,第i到第j个元素(1 <= i <= j <= n),就可以把j - i + 1个元素的总和看成一个元素(转化的过程), 然后,对n个这样的元素求最大子段和即可. 这一部分的时间复杂度是O(N^3),因此总复杂度也为O(N^3).

3.变形2:最大子立方体
例子:HOJ 2555 Eating Watermelon

有了上面的转化思路,这个问题也就很简单了,我们仍然应该进行转化. 这时,可以把平面上的一个子矩形看成一个元素,问题也轻松的转化成为一维的模型. 不过值得一提的是这时的预处理,可以设rec[x,y,z]表示z轴坐标为z的水平面中矩形(1,1,x,y)的数和。则z轴坐标为z的水平面中左上角为(x1,y1)、右下角为(x2,y2)的矩阵的数和为rec[x2,y2,z] + rec[x1,y1,z] - rec[x2,y1,z] - rec[x1,y2,z](转自zhouguyue最大子段和问题的报告) 这个方法我是看了报告才知道的. 这个预处理过程是O(N^3),DP时的复杂度是O(N^5) (枚举一个平面上所有的子矩形,O(N^4); 求最大子段和,O(N); 相乘知总复杂度为O(N^5)),总复杂度也是O(N^5).

PS: 这个题我的代码跑了1.4X秒,差不多是最快的二倍,不知道这里有没有什么较好的优化方法!

4.变形3:最大m子段和
例子: HDU 1024 Max Sum Plus Plus

这个变形和前面的稍有不同. 前面的背包问题中曾提到,"在限制条件增加一维时,可以将状态也相应的增加一维,来进行状态转移". 我觉得这个问题正是利用了这种强大思想! 可以增加一维状态,以dp[i][j]表示以第i个元素为结尾,使用j个子段所能达到的最大值 (这一维的状态,正是对应了新的限制条件!) 这样就很容易写出状态转移方程: dp[i][j] = max{ dp[i - 1][j] + a[i] (把第i个元素包含在最后一个子段内), dp[i - k][j - 1] + a[i], j - 1 <= k < n - m + j(第i个元素单独为一子串).


不过,这里还有一个优化,是今天才学会的:为了避免重复计算(这也是DP的动机),可以令p[i][j]表示前i个元素的最大j子段和,这样p[i][j]
= max{p[i - 1][j], dp[i][j]}, dp[i][j] = max{f[i - 1][j], p[i - 1][j - 1]} + a[i]. 这个优化没有实践过,不知道效果如何!

最大子段和问题是又一类基本的DP模型. 这段学习感受最深的就是对问题本质的把握. 2,3,4都是由最大子段和问题变形而来, 经过处理,都可以用一维模型的思想求解. 由此可见,如果能抓住问题的本质,合理的将问题进行转化,就可以用一个模型解决很多本质相同的问题!

你可能感兴趣的:(动态规划经典(最大子段和))