在上一篇文章中,我们讲解了「子数组」类动态规划题目的常见技巧。这篇文章继续讲解动态规划问题中的小技巧。今天要讲的是「如何定义多个子问题」。
常规的动态规划问题只需要定义一个子问题即可。然而在某些情况下,把子问题拆成多个会让思路更清晰。如果你没用过这个技巧的话,不妨跟着今天的例题来学习学习。
本篇文章的内容包括:
如何拆分动态规划的子问题
「最长波形子数组」问题的解法
度假问题的解法
多个子问题与二维子问题的转换关系
最长波形子数组
我们用「最长波形子数组」的解题过程来展示定义多个子问题在解题中的作用。
LeetCode 978. Longest Turbulent Subarray 最长波形子数组(Medium)
当
A
的子数组A[i..j]
满足下列条件之一时,我们称其为波形子数组:对于 i <= k < j,当 k 为奇数时,
A[k] > A[k+1]
,当 k 为偶数时,A[k] < A[k+1]
;或者:对于 i <= k < j,当 k 为偶数时,
A[k] > A[k+1]
,当 k 为奇数时,A[k] < A[k+1]
。也就是说,如果比较符号在子数组中的每个相邻元素对之间翻转,则该子数组是波形子数组。
返回 A 的最长波形子数组的长度。
首先我们要明白「波形子数组」的含义。(吐槽一句,官方把 trubulent 翻译成「湍流」,这翻译是给人看的吗?)我们关注的是数组中相邻元素之间的大小关系。如果后一个元素大于前一个元素,则是数组的「上升段」;反之,则是数组的「下降段」。那么,「波形子数组」就是一段交替上升下降的子数组。例如输入 [9, 4, 2, 10, 7, 8, 8, 1, 9]
中, [4, 2, 10, 7, 8]
是其中最长的一段波形子数组。
使用单个子问题求解
我们先看看使用传统的单个子问题该怎么求解这道题。
首先,看到题目中的「子数组」字样,我们应当立即想到子数组相关的解题技巧:在定义子问题的时候给子问题加上位于数组尾部的限制。
不理解这个解题技巧的同学,可以回顾我的上一篇文章:
LeetCode 例题精讲 | 16 最大子数组和:子数组类问题的动态规划技巧
使用这个技巧,我们可以这样定义子问题:
子问题 表示「数组 A[0..k)
中,位于数组尾部的最长波形子数组」。
之所以要限制子问题中求的最长波形子数组位于数组尾部,是因为只有数组尾部的波形子数组才可以和新加入