这是《剑指offer》系列的第一篇,这个系列的文章主要用于记录看书时的一些感想,做个记录。
题目
输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。例:
思路
这里采用动态规划的思想:
- 第一步:做一个选择,基于这个选择产生一个或多个待解的子问题
这里用函数 表示以第 个数字结尾的子数组的最大和,其中 。注意到函数 的不同取值构成了原题可能解的全集。例:当 的时候,有 。
这里我们选择子数组的结束位置 ,并且假定这个选择是最优解。即:我们假设结束于下标为 的子数组的最大和是原数组中所有子数组的最大和。
选择了结束位置之后,数组下标 6 的右边不用再进行考虑。那么基于 产生子问题 ,意义是结束于下标为 的子数组的最大和。
- 第二步:证明问题具有最优子结构性质,并得到递归式
使用“剪切 - 粘贴”反证法,证明在 的条件下问题具有最优子结构性质:
假设 不是数组区间 内结束于下标为 的子数组的最大和,那么我们将 从 中剪切去掉,并把最优解 粘贴其中得到 , 则有 ,与“ 是最优解”矛盾,故原假设不成立,得证。
这里稍微有点绕,如下图:
这里我们证明的是: 区间在 的时候一定会被 区间包含。
注:这里约束 的意义在于,当子问题的解是负数的时候,即使它是子问题能达到的最大值,也会拖了原问题的后腿,还不如直接等于 。例如 的子问题 ,那结束于下标为 的子数组的最大和应该等于 本身,即 。这是来自于我们定义这个函数时隐含的约束
由此我们证明了原问题的最优解 “ 结束于下标为 的子数组的最大和 ” 包含了它的子问题 “ 结束于下标为 的子数组的最大和 ” 的最优解。我们称这个问题具有最优子结构。
得到以下递推式:
- 第三步:证明问题具有重叠子问题性质
如果递归算法反复求解相同的子问题,我们称最优化问题具有重叠子问题性质,这里我们采用子问题图来可视化子问题之间的依赖关系,其中节点代表子问题,弧代表调用关系:
例如规模为4的子问题在求解时会调用规模为3、2和1的子问题,而处理规模为3的子问题的时候又会调用规模为2和1的子问题,造成高昂的计算代价。
- 第四步:使用动态规划进行求解
动态规划求解分为带备忘录的自顶向下法和带表格的自底向上法。
以下附上剑指offer提供的自底向上法:
结语
动态规划的题目常见,做多了题,有时候稀里糊涂地套上去试一下能做出来。但一直不懂背后的原理。这篇主要是用《算法导论》里面的分析手法分析了下《剑指offer》里的题,得到了些粗浅的看法,以后还要多加练习。
Reference:
《算法导论》p216
《剑指offer》p171