每日练题之动态规划(子序列问题讲解 1.最长递增子序列 2.摆动序列)

前言:

需要对「子序列」和「子数组」这两个概念进行区分;

    子序列(subsequence):子序列并不要求连续,但是我们调出来的顺序必须和原数组的顺序相同。例如:序列 [4, 6, 5] 是 [1, 2, 4, 3, 7, 6, 5] 的一个子序列;
    子数组(substring、subarray):子串一定是原始字符串的连续子串。

「上升」的意思是「严格上升」。反例: [1, 2, 2, 3] 不能算作「上升子序列」;
子序列中元素的 相对顺序 很重要,子序列中的元素 必须保持在原始数组中的相对顺序。如果把这个限制去掉,将原始数组去重以后,元素的个数即为所求;

子序列问题的一般做题步骤:

步骤概述:

  1. 定义状态(状态转移方程)
    动态规划首先需要明确问题的状态(通常是一个二维或一维数据库)。状态数据库dp[i]表示到达位置i时的最优化解(例如最小/顶点、最短/终止路径等) )。

  2. 填表
    根据状态转移方程,通过自下而上的方式进行dp内存填充,每个初始子问题的最优化解。

  3. 逆推最后一个子序列
    一旦dp表单状态填充完成,通常需要从dp最终形成(例如dp[n-1],这里的n可能是吞吐量的长度或其他标志性参数)开始,逆推找到最终解的子序列。

具体操作步骤:

1.定义状态和状态转移方程

假设我们在解决一个典型的动态规划问题,比如终止递增子序列(LIS)问题。我们定义一个队列dp,其中dp[i]以第i一个元素代表终止的终止递增子序列的长度。状态移位方程如下:

  • dp[i] = max(dp[j] + 1),其中0 <= j < iarr[j] < arr[i]
2. 填充状态

根据状态转移方程,自底向上填充dp队列,最终得到递增子序列的长度。假设dp[n-1]表示到达队列最后一个元素时的最优解。

3. 逆推得到子序列

一旦我们知道了dp阵列中的最优值,接下来就是逆推回去,找到具体的子序列。例如:

  • 从最后一个元素开始:找到dp顶点中的对应索引,假设是i,即我们从arr[i]开始。
  • 找到前面的一个元素:逆推找到前面的一个元素arr[j]arr[j] < arr[i]且使得dp[j] == dp[i] - 1。这样就可以把arr[j]加入到子序列中。
  • 重复这个过程:继续沿着dp阵列逆推,直到找到整个子序列。

 1.题目讲解

1. 最长递增子序列

1.题目解析

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

2.讲解算法原理

1.状态表示:

dp[i] : 表示当我们到达i位置时得到的最长递增子序列。

2.状态转移表示:

我们的dp[i]有两种表示:

1.当数组长度为1时:dp[i] = 1;

2.当数组长度不为1时:

我们以i位置为结尾时:因为子序列 是由数组派生而来的序列,:子序列并不要求连续,但是我们调出来的顺序必须和原数组的顺序相同。例如:序列 [4, 6, 5] 是 [1, 2, 4, 3, 7, 6, 5] 的一个子序列;但是:序列 [4, 6, 5]不是严格按照递增。

所以要找到严格递增的话,我们必须需要让我们后一个值大于前一个值,又因为我们不只是锁定为从0下标位置开始找最长子序列,这个值可以是数组的任意一个下标。

例如示例1:

nums = [10,9,2,5,3,7,101,18]

从2 开始找 而不是从10开始,由此我们可以得出当我们以 i 下标为结尾时,我们要找到 i - 1下标的状态:

i

他前面的值我们总体用J来表示:即:0<= j <= i - 1 位置的值,如果num[ i ] (后一项) > num[j](前一项) 我们就让这个值+1,求出最大之即可

3.初始化

表里面的所有值初始化为 1

4.填表顺序

从左往右

5.返回值

dp 表里面的最大值

3.编写代码

2.摆动序列

1.题目解析

 

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

  • 例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

  • 相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 最长子序列的长度

2.讲解算法原理

1.状态表示:

第一次分析:

dp[i] 表示为 i 位置的元素为结尾的最大湍流子数组的长度;

当我们只定义一个状态表示时,我们会发现用它表示时dp[i -1]的数字有两种状态

1.比后一位大 2.比前一位小 所以不行

第二次分析:

因为dp[i]的前一位有两种表现形式,所以我们要定义两种状态表示

f[i] 表示:以ì位置元素为结尾的所有的子序列中,最后一个位置呈现"上升"趋势的最长摆动序列的长度
g[i] 表示:以i位置元素为结尾的所有的子序列中,最后一个位置呈现“下降”趋势的最长摆动序列的长度

2.状态转移方程;
0    1    2    ......    i-2    i-1    i
                    b    a

 1.当最后一位呈现“上升”状态的 情况分析:

1.当数组长度为1时:f[i] = 1;

2.当数组长度不为1时:num[ i ] (后一项) > num[j] (前一项) 时 f[i] = Math.max(g[i -1] +1,f[i]);

 2.当最后一位呈现“下降”状态的 情况分析:

1.当数组长度为1时:g[i] = 1;

2.当数组长度不为1时:num[ i ] (后一项) < num[j] (前一项) 时 g[i] =Math.max( f[i -1] +1,g[i]);


3.初始化:

1.因为只有一个元素时,长度为一,所以我们把初始值都设为一,即可省略为1的这种情况,


4.填表顺序:

从左往右,两个表一起填

5.返回值:

F表和G表里的最大值.

3.编写代码

总结:

子序列问题和子数组问题大致的思路方式是不变的,如果碰到相类似的题我们就可以按部就班,想我们前几天做的题,就可以完美解决问题,如果一个状态表示不够我们就可以定义多个状态表示来解决问题;

下期预告:

感兴趣的小伙伴们可以先看看题呦,一起加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

study hard_day

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值