| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 动态规划——子序列问题——连续?不连续? -> 正文阅读 |
|
[数据结构与算法]动态规划——子序列问题——连续?不连续? |
动态规划——子序列问题——连续?不连续? 子序列问题是动态规划的一个常考题型,本篇文章主要介绍 子序列(连续)和子序列(不连续)两个问题。连续和不连续指的是子序列是否是连续的,还是说中间是可以有间隔的子序列。 一:子序列(不连续) ? ? 分析:从题意我们可以读出该题想求的是不连续的递增子序列。 解法:动态规划 第一步:dp[i]的定义 dp[i]表示i之前包括i的最长上升子序列的长度。 第二步:状态转移方程 位置 i 的最长升序子序列等于 j 从 0 到 i-1 各个位置的最长升序子序列 + 1 的最大值。 所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); 注意这里不是要dp[i] 与 dp[j] + 1进行比较,而是我们要取dp[j] + 1的最大值。 第三步:dp[i]的初始化 每一个i,对应的dp[i](即最长上升子序列)起始大小至少都是1. 第四步:确定遍历顺序 dp[i] 是有 0 到 i-1 各个位置的最长升序子序列推导而来,那么遍历 i 一定是从前向后遍历。 j 其实就是 0 到 i-1,遍历i的循环在外层,遍历j则在内层。 代码如下:
? ? 分析: 第一步:确定dp数组(dp table)以及下标的含义 dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j] 第二步:确定递推公式 主要就是两大情况: ①text1[i - 1] 与 text2[j - 1]相同 ②text1[i - 1] 与 text2[j - 1]不相同 如果① text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1; 如果② text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。 即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 第三步:dp数组如何初始化 先看看dp[i][0]应该是多少呢? text1[0, i-1]和空串的最长公共子序列自然是0,所以dp[i][0] = 0; 同理dp[0][j]也是0。 其他下标都是随着递推公式逐步覆盖,初始为多少都可以,那么就统一初始为0。 第四步:确定遍历顺序 从递推公式,可以看出,有三个方向可以推出dp[i][j],如图: ? 那么为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后,从上到下来遍历这个矩阵。 代码如下:
? ? 分析:仔细读题会发现其实本题就是求最长公共子序列(不连续)和上题一模一样,换一下参数就可以直接AC。? 代码如下:
二:子序列(连续) ? 第一步:确定dp数组(dp table)以及下标的含义 dp[i]:以下标i为结尾的数组的连续递增的子序列长度为dp[i]。 注意这里的定义,一定是以下标i为结尾,并不是说一定以下标0为起始位置。 第二步:确定递推公式 如果 nums[i] > nums[i-],那么以 i?为结尾的数组的连续递增的子序列长度 一定等于 以i-1为结尾的数组的连续递增的子序列长度 + 1 。 即:dp[i] = dp[i-1] + 1; 因为本题要求连续递增子序列,所以就必要比较nums[i + 1]与nums[i],而不用去比较nums[j]与nums[i] (j是在0到i之间遍历)。 既然不用j了,那么也不用两层for循环,本题一层for循环就行,比较nums[i] 和 nums[i-1]。 第三步:dp数组如何初始化 以下标i为结尾的数组的连续递增的子序列长度最少也应该是1,即就是nums[i]这一个元素。 所以dp[i]应该初始1; 第四步:确定遍历顺序 从递推公式上可以看出, dp[i]依赖dp[i-1],所以一定是从前向后遍历。 代码如下:
分析: 第一步:确定dp数组(dp table)以及下标的含义 dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 第二步:确定递推公式 根据dp[i][j]的定义,dp[i][j]的状态只能由dp[i - 1][j - 1]推导出来。 即当A[i - 1] 和B[j - 1]相等的时候,dp[i][j] = dp[i - 1][j - 1] + 1; 根据递推公式可以看出,遍历i 和 j 要从1开始! 第三步:dp数组如何初始化 根据dp[i][j]的定义,dp[i][0] 和dp[0][j]其实都是没有意义的! 但dp[i][0] 和dp[0][j]要初始值,因为 为了方便递归公式dp[i][j] = dp[i - 1][j - 1] + 1; 所以dp[i][0] 和dp[0][j]初始化为0。 第四步:确定遍历顺序 外层for循环遍历A,内层for循环遍历B。或者相反都可以。 同时题目要求长度最长的子数组的长度。所以在遍历的时候顺便把dp[i][j]的最大值记录下来。 代码如下:
总结:这道题我觉得和子序列(不连续)里面的第二题非常像,只不过这道题要求必须是联系的,所以只有在 nums1[i-1] == nums2[j-1] 时,dp[i][j] ==?dp[i-1][j-1] + 1,如果不相等就无需记录了,默认值1即可。但是上面的第二题,还需要比较dp[i][j-1] 和?dp[i-1][j] 的最大值进行记录,因为之后如果有相等的还需要前面的计算结果。而本题一旦不相等,这个子数组就断了,无需记录之前的数据。 分析:这个题之前用贪心也写过,但是这里还是使用动态规划来进行解决,下面我会贴上贪心的代码。 第一步:确定dp数组(dp table)以及下标的含义 dp[i]:包括下标i之前的最大连续子序列和为dp[i]。 第二步:确定递推公式 dp[i]只有两个方向可以推出来:
一定是取最大的,所以dp[i] = max(dp[i - 1] + nums[i], nums[i]); 第三步:dp数组如何初始化 从递推公式可以看出来dp[i]是依赖于dp[i - 1]的状态,dp[0]就是递推公式的基础。 dp[0]应该是多少呢? 根据dp[i]的定义,很明显dp[0]应为nums[0]即dp[0] = nums[0]。 第四步:确定遍历顺序 递推公式中dp[i]依赖于dp[i - 1]的状态,需要从前向后遍历。 ?动态规划代码如下:
贪心代码如下:
总结:动态规划在解决子序列问题这里有两把刷子,以后遇到子序列的问题都可以考虑一下使用动态规划来进行解决。 如果有写的不对的地方,欢迎指出。感谢代码随想录。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 16:38:24- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |