动态规划—最长上升子序列(POJ 1458)

(1)字符子串指的是字符串中连续的n个字符,如abcdefg中,ab,cde,fg等都属于它的字串。

(2)字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序。如abcdefg中,acdg,bdf属于它的子序列,而bac,dbfg则不是,因为它们与字符串的字符顺序不一致。

知道了这个,数值的子序列就很好明白了,即用数组成的子序列。这样的话,最长上升子序列也很容易明白了,归根结底还是子序列,然后子序列中,按照上升顺序排列的最长的就是我们最长上升子序列了,这样听来是不是就很容易明白啦~

还有一个非常重要的问题:请大家用集合的观点来理解这些概念,子序列、公共子序列以及最长公共子序列都不唯一,但很显然,对于固定的数组,虽然LIS序列不一定唯一,但LIS的长度是唯一的。再拿我们刚刚举的栗子来讲,给出序列 ( 1, 7, 3, 5, 9, 4, 8),易得最长上升子序列长度为4,这是确定的,但序列可以为 ( 1, 3, 5, 8 ), 也可以为 ( 1, 3, 5, 9 )。


好了,知道什么是最长上升子序列之后,我们来看看怎么求长度。

LIS的解法有好几种,例如O(n^2)的DP,O(nlogn)的二分+贪心法,以及O(nlogn)的树状数组优化的DP。

在这里我详细介绍第一种,动态规划的解法。

动态规划解法

我们都知道,动态规划的一个特点就是当前解可以由上一个阶段的解推出, 由此,把我们要求的问题简化成一个更小的子问题。子问题具有相同的求解方式,只不过是规模小了而已。最长上升子序列就符合这一特性。我们要求n个数的最长上升子序列,可以求前n-1个数的最长上升子序列,再跟第n个数进行判断。求前n-1个数的最长上升子序列,可以通过求前n-2个数的最长上升子序列……直到求前1个数的最长上升子序列,此时LIS当然为1。

让我们举个例子:求 2 7 1 5 6 4 3 8 9 的最长上升子序列。我们定义d(i) (i∈[1,n])来表示前i个数以A[i]结尾的最长上升子序列长度。

前1个数 d(1)=1 子序列为2;

前2个数 7前面有2小于7 d(2)=d(1)+1=2 子序列为2 7

前3个数 在1前面没有比1更小的,1自身组成长度为1的子序列 d(3)=1 子序列为1

前4个数 5前面有2小于5 d(4)=d(1)+1=2 子序列为2 5

前5个数 6前面有2 5小于6 d(5)=d(4)+1=3 子序列为2 5 6

前6个数 4前面有2小于4 d(6)=d(1)+1=2 子序列为2 4

前7个数 3前面有2小于3 d(3)=d(1)+1=2 子序列为2 3

前8个数 8前面有2 5 6小于8 d(8)=d(5)+1=4 子序列为2 5 6 8

前9个数 9前面有2 5 6 8小于9 d(9)=d(8)+1=5 子序列为2 5 6 8 9

d(i)=max{d(1),d(2),……,d(i)} 我们可以看出这9个数的LIS为d(9)=5

总结一下,d(i)就是找以A[i]结尾的,在A[i]之前的最长上升子序列+1,当A[i]之前没有比A[i]更小的数时,d(i)=1。所有的d(i)里面最大的那个就是最长上升子序列。

**状态设计:**F [ i ] 代表以 A [ i ] 结尾的 LIS 的长度

**状态转移:**F [ i ] = max { F [ j ] + 1 ,F [ i ] } (1 <= j <  i,A[ j ] < A[ i ])

**边界处理:**F [ i ] = 1 (1 <= i <= n)

**时间复杂度:**O (n^2)


具体例子

这里我们以 北大OJ (POJ 2533)最长上升子序列 为例子

描述

ai的数字序列如果a1 < a2 <…<一个。设给定数字序列(a1, a2,…)的子序列, aN)为任意序列(ai1, ai2,…, 1 <= i1 <= i2 <…< iK <= n。例如,序列(1,7,3,5,9,4,8)已经排序了子序列,如(1,7),(3,4,8)等。所有最长有序子序列的长度都是4,例如(1,3,5,8)。

当给定数字序列时,程序必须找到最长有序子序列的长度。

输入

输入文件的第一行包含序列N的长度。第二行包含序列的元素—N个整数,每个整数的范围是0 ~ 10000,用空格隔开。1 <= n <= 1000

输出

输出文件必须包含单个整数-给定序列的最长有序子序列的长度。

样例输入

7

总结

总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。

如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。

如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。

[外链图片转存中…(img-ygy90owg-1721835726659)]

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值