最长递增子序列问题

  • 简述

    • 经典的动态规划问题。
  • 问题描述

    • 给定一个序列,求解其中长度最长的递增子序列。
  • 问题分析

    • 这种可以向下查询答案的很容易想到动态规划的解法。
    • 要求长度为i的序列Ai={a1,a2,a3,ai}的最长递增子序列,需要先求出序列Ai-1{a1,a2,a3,ai-1}中各元素(a1,a1,ai-1)作为最大元素的最长递增子序列,然后将这i-1个递增序列与ai进行比较。如果某个长度为m的序列的末尾元素aj(j<i)比ai小,则将元素ai加入这个递增子序列,得到一个长度为m+1的新序列,否则其长度不变,将处理后的i-1个序列的长度进行比较,最长的就是要求的最长递增子序列。
    • 举个例子
      • 对于序列A{3,1,4,5,9,2,6,5,0},当处理到第7个元素“6”时,i-1个序列为。

        • 3
        • 1
        • 3,4
        • 3,4,5
        • 3,4,5,9
        • 1,2
      • 经过上述处理后,序列变为如下。有两个最长的。可见,以“6”为结尾的最长递增子序列为{3,4,5,6}。
        • 3,6
        • 1,6
        • 3,4,6
        • 3,4,5,6
        • 3,4,5,9
        • 1,2,6
      • 同样,处理到第8个元素“5”的时候,原始序列处理后变为如下。
        • 3,5
        • 1,5
        • 3,4,5
        • 3,4,5
        • 3,4,5,9
        • 1,2,5
      • 可见,以第8个元素“5”为结尾的最长递增子序列为{3,4,5}。
      • 最后一个0,无法在后面添加,因此,这个题目的答案为{3,4,5,9}。序列A的最长递增子序列长度为4.
      • 注意:求解到的最长递增子序列可能不止一个。
    • 设f(i)表示序列中以ai为末尾元素的最长递增子序列的长度,则在求以ai为末尾元素的最长递增子序列是,找到所有序号在i前面且小于ai的元素aj,即j<i且aj<ai。
      • 如果这样的元素存在,那么对所有的aj,都有一个以aj为末尾元素的最长递增子序列的长度f(j)。把其中最大的f(j)找出来,f(i)就等于这个最大的f(j)+1。也就是说,以ai为末尾元素的最长递增子序列,等于在使f(j)最大的那个aj为末尾元素的递增子序列最后再加上ai;如果这样的元素不存在,那么自身构成一个长度为1的以ai为末尾元素的递增子序列。
  • 代码

    •   def f(arr):n = len(arr)dp = [0] * nfor i in range(n):dp[i] = 1for j in range(i):if arr[j] < arr[i]:dp[i] = max(dp[i], dp[j]+1)return dpdef generate_lis(arr, dp):n = max(dp)index = dp.index(n)lis = [0] * nn -= 1lis[n] = arr[index]# 从右向左查for i in range(index, -1, -1):if arr[i] < arr[index] and dp[i] == dp[index] - 1:n -= 1lis[n] = arr[i]index = ireturn lisif __name__ == '__main__':arr = [3, 1, 4, 5, 9, 2, 6, 5, 0]print(generate_lis(arr, f(arr)))
      
    • 这种不确定向下查找的方向的不适合使用递归。
  • 算法改进

    • 上面那种解法的复杂度达到了O(n^2),其实可以改进为O(nlogn)。
    • 在基本算法中,当需要计算前i个元素的最长递增子序列的时候,前i-1个元素作为最大元素的i-1个递增序列,无论是长度,还是最大元素值,都毫无规律,所以在开始计算前i个元素的时候只能遍历前i-1个元素,以找到满足的j值,使得aj<ai且满足条件的j中,以aj作为最大元素的递增子序列最长。
    • 其实。在一个序列中,长度为n的递增子序列可能不只有一个,但是最大元素最小的递增子序列只有一个。上面例子中,当处理第7个元素“6“时,长度为2的子序列有两个,即{3,4}和{1,2},这个{1,2}就是最大元素最小的递增子序列。(可以轻易证明唯一性)随着序列长度的变化,满足条件的子序列不断变化。
    • 如果将这些子序列安装长度由短到长排序,将每个序列的最大元素放在一起,形成新序列B{b1,b2,bj},则序列B一定是递增的。(此处不证明)
    • 发现了这个严格递增,眼前一亮,可以利用此序列的严格递增进行二分查找,找到最大元素刚好小于aj的元素bk,将aj加入这个序列尾部,形成长度为k+1但是最大元素又小于b(k+1)的新序列,取代之前的b(k+1)。如果aj比B中所有元素都大,说明发现了以aj为最大元素,长度为n+1的递增序列,将aj作为B(n+1)的第n+1个元素。从b1依次递推,可以在O(nlogn)时间内找出A的最长递增子序列。
    • 这种方法大幅度提高了运行效率。
    • 优化代码见我的Github
  • 补充说明

    • 具体代码可以查看我的Github,欢迎Star或者Fork
    • 参考书《你也能看得懂的Python算法书》,书中略微有一点不合理之处,做了修改
    • 到这里,其实你已经体会到了动态规划的简约之美,当然,要注意Python是有递归深度限制的,如不是必要,建议使用循环控制

动态规划算法-04最长递增子序列问题相关推荐

  1. 用动态规划算法实现最长公共子序列问题的算法(java实现)

    用动态规划算法实现最长公共子序列问题的算法 public class longestCommonSubsequence {//构造追踪数组rec,记录子问题来源private static Strin ...

  2. 【算法】最长递增子序列问题

    1.问题描述 有一个序列 A = [18, 17, 19, 6, 11, 21, 23, 15],求出最长递增子序列的个数. 2.算法描述 该问题解唯一,但解的形式不唯一.譬如,该问题最长递增子序列的 ...

  3. 动态规划算法---求最长公共子序列

    最长公共子序列和最长公共子串区别        最长公共子串(Longest Common Substring)与最长公共子序列(Longest Common Subsequence)的区别: 子串要 ...

  4. (算法)最长递增子序列

    问题: Given an array of N integer, find the length of the longest increasing subsequence. For example, ...

  5. 【LeetCode 动态规划专项】最长递增子序列的个数(673)

    文章目录 1. 题目 1.1 示例 1.2 说明 1.3 提示 1.4 进阶 2. 解法一(动态规划) 2.1 分析 2.1.1 定义状态 2.1.2 初始化状态 2.1.3 状态转移 2.1.4 返 ...

  6. 动态规划算法解最长公共子序列LCS问题

    动态规划算法解LCS问题 作者 July 二零一零年十二月三十一日 本文参考:微软面试100题系列V0.1版第19.56题.算法导论.维基百科. 第一部分.什么是动态规划算法 ok,咱们先来了解下什么 ...

  7. leetcode算法题-- 最长递增子序列的个数★

    原题链接:https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/ 相关题目:最长上升子序列 lengths ...

  8. 动态规划算法 | 最长递增子序列

    通过查阅相关资料发现动态规划问题一般就是求解最值问题.这种方法在解决一些问题时应用比较多,比如求最长递增子序列等. 有部分人认为动态规划的核心就是:穷举.因为要求最值,肯定要把所有可行的答案穷举出来, ...

  9. 动态规划算法04-最长递增子序列问题

    最长递增子序列问题 简述 经典的动态规划问题. 问题描述 给定一个序列,求解其中长度最长的递增子序列. 问题分析 这种可以向下查询答案的很容易想到动态规划的解法. 要求长度为i的序列Ai={a1,a2 ...

最新文章

  1. 【错误记录】IntelliJ IDEA 编译 Groovy 报错 ( GroovyRuntimeException: This script or class could not be run. )
  2. maven spring profile 协同
  3. 交通注意:叉车和自行车
  4. Golang学习笔记——Slice
  5. linux培训机构 网络班,Linux基础教程之网络基础知识与Linux网络配置
  6. LeetCode 241. 为运算表达式设计优先级(动态规划)
  7. 微信小程序(应用号)调试工具内测破解方法
  8. Spring:@Configuration和@Component的使用与区别
  9. 解决Windows Vista/7/8/8.1 远程桌面连接XP或2003缓慢的问题
  10. 【总结】动态规划 or 组合数学解决棋盘(迷宫)路径问题(持续更新中)
  11. yagmail发送附件
  12. linux下exe软件反编译工具下载,ilspy.exe
  13. 视频图像格式YUV详解
  14. Lua C API中文函数手册
  15. Linux环境下右键无法新建文档的解决方法——Ubuntu 16.x
  16. c语言null是什么意思,c语言null什么意思?
  17. 呕心编写的《金牌网管师——金牌网吧网管》
  18. 第27次CCF-CSP计算机软件能力认证(2022-09-18)
  19. 基于51单片机HX711的电子秤称重计价proteus仿真程序设计
  20. conda创建环境报错conda.core.subdir_data.Response304ContentUnchanged

热门文章

  1. MyBatis 实际使用案例-dataSource
  2. MybatisPlus性能分析插件
  3. 数据库-数据库的备份与恢复
  4. Hello Python程序演练
  5. SpringBoot_配置-@PropertySource、@ImportResource、@Bean
  6. 物理设计-如何存储日期类型
  7. 设计模式之_动态代理_03
  8. oracle与jdbc连接数据库,JDBC与Oracle数据库连接
  9. 【小题目】写JAVA程序时可以创建一个名为123.java的源文件吗
  10. 《CDN 之我见》原理篇——CDN的由来与调度