前言:
在算法与数据结构的广袤天地中,动态规划犹如一颗璀璨的明珠,散发着独特的魅力与光芒。它以巧妙的方式解决了众多复杂的优化问题,其中子序列问题更是动态规划领域中极具代表性且应用广泛的一类。
随着信息技术的飞速发展,无论是在计算机科学的核心领域,如软件开发、人工智能、数据处理,还是在金融、生物信息学、运筹学等跨学科的应用场景里,对子序列相关算法的深入理解与熟练掌握都变得愈发关键。解决子序列问题能够帮助我们在海量数据中挖掘出有价值的信息模式,优化资源分配,提升系统效率,做出更明智的决策。
本系列的 “每日练题之动态规划(子序列问题讲解)” 旨在为广大编程爱好者、算法学习者以及相关专业人士提供一个系统深入学习子序列问题动态规划解法的平台。我们将通过每日一题的方式,逐步剖析不同类型子序列问题的本质特征、解题思路以及代码实现细节。从基础的最长递增子序列问题开始,逐步拓展到更复杂的如最长公共子序列、不连续子序列求和最大值等变体问题,带领大家领略动态规划在子序列问题处理上的精妙之处。
在这个过程中,我们不仅会注重理论知识的讲解,还会通过大量实际案例和详细的代码注释,帮助大家更好地理解每一个步骤背后的逻辑,从而能够举一反三,灵活应对各种实际应用中的子序列挑战。无论你是初涉算法领域的新手,渴望打下坚实的基础,还是已经有一定经验的开发者,希望进一步提升自己在动态规划方面的技能,相信本系列内容都将为你带来满满的收获与启发。
让我们一同踏上这充满挑战与惊喜的动态规划(子序列问题讲解)学习之旅,在代码的世界里探索子序列的奥秘,提升自我,为解决更复杂的编程和实际问题积蓄力量。
今日讲题:
1.最长定差子序列
1.题目讲解
给你一个整数数组 arr
和一个整数 difference
,请你找出并返回 arr
中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference
。
子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr
派生出来的序列。
2.讲解算法原理
1.状态表示:
1.定状态
dp[i] : 以i位置的元素结尾时,我们所得到最长定差子序列的长度;
2.状态转移表示:
当我们以i位置为结尾时:
1 | 2 | 3 | 4 |
i | |||
j-1 |
如果我们的前一项与后一项的差值为difference时,我们就将前一项符合的数字统计到我们的dp[i]中:
注意:当我们i每到一个下标位置时;我们的j都要重新从0下表开始遍历数组来找到符合的数据
因为这个示例简单我就只枚举了最后一个,在大家枚举复杂情况时,要将0~i的这段数据全枚举一下。
当 i 数组位置为1时:dp[1] = 2 ;dp[1] - dp[0] =2 - 1 = 1(difference)于是我们就统计下:
dp[1] = Math.max(dp[1],dp[0]+1); 此时dp[1] = 2;
当 i 数组位置为2时:dp[2] = 3 ;dp[2] - dp[1] =3 - 2 = 1(difference)于是我们就统计下:
dp[2] = Math.max(dp[2],dp[1]+1); 此时dp[2] = 3;
当 i 数组位置为3时:dp[3] = 4 ;dp[3] - dp[2] =4 - 3 = 1(difference)于是我们就统计下:
dp[3] = Math.max(dp[3],dp[2]+1); 此时dp[3] = 4;
当我们所有元素按照第一个元素排序后,我们只需要判断后一项的数字减去前一项值为difference
即可:
但是当我们按照这种情况去写时,提交后却发现一下情况:
所以我们按照上面的思路,用map来解决:
dp[i] = 1 // 表示在 i 之前没有出现等差子序列
dp[i] = dp[i - difference] + 1 // 表示在 i 之前出现了等差子序列,长度为 dp[i - difference], 而 i 也是满足这个等差序列的,所以等差序列的长度在此基础上加 1 就可以了
3.初始化
表里面的所有值初始化为 1
4.填表顺序
从左往右
5.返回值
dp 表里面的最大值
3.编写代码
2.最长的斐波那契子序列的长度
1.题目解析
如果序列 X_1, X_2, ..., X_n
满足下列条件,就说它是 斐波那契式 的:
n >= 3
- 对于所有
i + 2 <= n
,都有X_i + X_{i+1} = X_{i+2}
给定一个严格递增的正整数数组形成序列 arr ,找到 arr 中最长的斐波那契式的子序列的长度。如果一个不存在,返回 0 。
(回想一下,子序列是从原序列 arr 中派生出来的,它从 arr 中删掉任意数量的元素(也可以不删),而不改变其余元素的顺序。例如, [3, 5, 8]
是 [3, 4, 5, 6, 7, 8]
的一个子序列
2.讲解算法原理
1.状态表示:
1.定状态
dp[i] : 以i位置的元素结尾时,我们所得到最长的斐波那契子序列的长度的长度;
2.分析状态
当我们以i位置为结尾时:
0 | …… | j - 2 | …… | i |
我们现在可以确定的状态是:
(a) arr[i] 和 (b) arr[j] 当后一项减前一项成立时我们可以得到:第一项为:b - (a - b)他两项的差值 = 2b - a ;从而我们又可以得到前前一项的值3b - 2a,此时这些数就构成了等差数列 ;但是当我们这样定义后我们虽然可以正常得出结果,但是却不无法累加来确定我们的长度了
dp[i] = Math.max(dp[i],dp[j]+1) 因为此时我们的 j 下标被定义成一个特殊的值了
所以这样定义无法让我们解题,但是如果我们直接确定两个值来分析是否可以呢?
dp[i][j] : 以i位置和j的元素结尾时,我们所得到最长的斐波那契子序列的长度的长度;
2.状态转移表示:
当我们以i位置和j的元素结尾时:
0 | …… | …… | …… | i |
j |
设与他俩构成等差数列的数字为K;
此时k可能出现的位置有:
1 .在j的前面: nums[k] < nums[j] 此时条件成立dp[k][i]+1;
2.在j和i中间: nums[j]<num[k]<nums[i] (不考虑)2
3.数字k不存在 2
优化:
将数组中所有元素与它们的下标绑定,存在哈希表中
3.初始化
表里面所有的值都初始化为 2
4.填表顺序
从上往下
5.返回值
表里面的最大值 ret,因为表里面所有的值都初始化为 2,所以当我们的ret < 3 时,就说明此表中不含有斐波那契子序列 返回0即可
3.编写代码
上期链接:
每日练题之动态规划(子序列问题讲解 1.最长递增子序列的个数 2.最长数对链)-优快云博客
下期预告:
感兴趣的小伙伴们可以先看看题呦,一起加油!!!