前言:
在算法的广袤天地中,动态规划犹如一颗璀璨的明珠,散发着独特的智慧光芒。而子序列问题,则是动态规划领域中极具代表性且应用广泛的重要板块。
每日练题,是我们在编程与算法学习之路上的坚实脚印。在深入探索动态规划的旅程里,子序列问题犹如一座充满挑战与奥秘的山峰等待我们攀登。通过对其细致入微的讲解与练习,我们将逐步揭开动态规划处理子序列问题的神秘面纱。无论是最长递增子序列那优雅的递推思路,还是公共子序列蕴含的巧妙状态转移,都将在我们的每日钻研中清晰呈现。
在这里,你将见证如何运用动态规划的思想,将看似复杂多变的子序列问题拆解为一个个可解的子任务,以精准的状态定义、合理的状态转移方程构建起高效的解题框架。每一道精心挑选的练习题,都是我们磨砺思维、提升算法能力的宝贵契机。让我们在每日练题中,沉浸式感受动态规划与子序列问题碰撞出的智慧火花,开启一场充实且富有成就感的算法学习之旅,为成为更卓越的编程者和算法高手奠定坚实的基础。
今日讲题:
1.最长递增子序列的个数
1.题目解析:
给定一个未排序的整数数组 nums
, 返回最长递增子序列的个数 。
注意 这个数列必须是 严格 递增的。
2.讲解算法原理
1 | 3 | 5 | 4 | 7 |
我们从头开始选,如果我们后一项大于前一项时,我们就开始统计这一串中的最长递增子序列。
例如我们拿到的第一串:1 - 3 - 5 - 7 我们记下这一串的长度 len = 4 ,当我们再次开始寻找时: 1 - 3 - 4 - 7 这时候我们就要判断和我们之前记得那组数据长度了:
1.如果(新子串的长度)Rmax == len 时 我们就让retcount++;
2. 如果(新子串的长度)Rmax <= len 时 可以忽略不计;
3. 如果(新子串的长度)Rmax >= len 时, 我们就要开始重新统计数量,并把之前统计的数量重新修改为初始值(1)即可 ;
1.状态表示:
由上所述:
我们只定义 dp[i] 表示:以i位置元素为结尾的所有的子序列中,最长递增子序列的个数 是不行的,
所以我们要定义:
len[i] 表示:以ì位置元素为结尾的所有的子序列中,最长递增子序列的"“长度"
count[i] 表示:以ì位置元素为结尾的所有的子序列中,最长递增子序列的“个数”
这两个状态表示来定义
2.状态转移表示:
1.当数组长度为 1 时 我们的 len[i] = 1
2.当数组长度不为1时 :
如果我们后一项大于前一项时,我们就开始统计这一串中的最长递增子序列。
但是在实例2中我们可以看到如果 前后两个值相同时我们也要统计:
1.(后一项) len[j] + 1 ==(前一项)len[j] 时 count[i] += count[j];
2.当(后一项) len[j] + 1 >(前一项)len[j] 时, len[i] = len[j] + 1; count[i] = count[j];
3.初始化:
因为只有一个元素时,长度为一,所以我们把初始值都设为一,即可省略为1的这种情况,
4.填表顺序:
从左往右,两个表一起填
5.返回值:
返回统计的数字recount
3.编写代码
2. 最长数对链
1.题目解析
给你一个由 n
个数对组成的数对数组 pairs
,其中 pairs[i] = [lefti, righti]
且 lefti < righti
。
现在,我们定义一种 跟随 关系,当且仅当 b < c
时,数对 p2 = [c, d]
才可以跟在 p1 = [a, b]
后面。我们用这种形式来构造 数对链 。
找出并返回能够形成的 最长数对链的长度 。
你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
2.讲解算法原理
1.状态表示:
预处理:按照第一个元素排序即可
Arrays.sort(pairs,(a,b)-> a[0] - b[0]);
dp[i] 表示:以i位置元素为结尾的所有的子序列中,最长数对链的长度的个数
2.状态转移表示:
当我们所有元素按照第一个元素排序后,我们只需要判断后一项的第一个数字大于前一项的第二个数字即可:
if(pairs[i][0] > pairs[j][1]) 如果满足了,我们就加1,不满足就返回当前符合的元素个数:
dp[i] =Math.max( dp[j]+1,dp[i]);
3.初始化
表里面的所有值初始化为 1
4.填表顺序
从左往右
5.返回值
dp 表里面的最大值
3.编写代码
上期链接:
每日练题之动态规划(子序列问题讲解 1.最长递增子序列 2.摆动序列)-CSDN博客
下期预告:
感兴趣的小伙伴们可以先看看题呦,一起加油!!!