最长上升子序列,最长公共子序列以及最长公共上升子序列问题

  1. 最长上升子序列问题:给定序列 a ,求 a 中上升子序列的最大长度。

状态表示:f[i]表示以a[i]结尾的上升子序列长度,属性为最大值。对于状态划分一般以 ‘最后’ 为依据,寻找状态转移方程。对于此题 ‘最后’ f[i]的值是否变化,当算f[i] 时,很显然要和倒数第二个比较来判断是不是递增。所以状态划分就以倒数第二个数为依据,即倒数第二个数不存在,倒数第二个数为a[1],为a[2]…为a[i-1]。所以状态转移方程为
倒 数 第 二 个 为 空 时 f [ i ] = 1 ;
if(a[i]>a[k])f[i]=max(f[i],f[k]+1)1<=k<=i−1)

  1. 最长上升子序列优化:贪心+二分,将n^2时间复杂度优化成nlogn。

思路:我们开一个数组q,下标代表子序列长度。对于任意一个子序列,如果某个数能加在它后面,必定能加在序列结尾的数字比它更小的那个序列中,所以对于某个长度的子序列我们只需要纪录序列结尾最小的那一个就行了。然后基于贪心的思想:我们每次尽量保留小的用大的去更新,因为小的适用范围更广。对于某个a[i],找到小于a[i]的最大值,把a[i]加到它后面,如果找不到小于a[i]的,则说明只能新开一个存a[i]。而且对于我们开的数组纪录的数字是有单调上升的,所以我们在找的时候可以用二分查找。对于单调性的证明如下:

对于q[i],q[i+1],假设q[i+1]<=q[i]的话,那么对于长度为i+1的序列,倒数第二个数必定小于q[i],所以必定可以用这个数去更新q[i],所以就与我们q数组的定义有矛盾了,因为我们定义的q数组存的就是子序列末尾值最小的数,所以假设就不成立。所以是单调上升的。证明完毕。

  1. 最长公共子序列问题:首先状态表示f[i , j],表示a序列前i个中,和b序列前j个中的公共子序列的长度,属性:最大值。

状态转移是状态划分根据“最后”的原则,很容易想到a[i] , b[j]选和不选,总共四种情况。对于a[i],b[j]都不选,很显然是f[i-1,j-1],对于都选就是f[i, j], 但对于选a[i],不选b[j],和不选a[i],选b[j],不能想当然的用f[i-1,j],表示,为什么呢,因为根据我们定义的状态含义是在a序列前i-1中,b序列前j中,选的最长公共子序列,但不一定非要选b[j],也就是说,不选a[i],选b[j]只是其中的一个子集,但我们依然可以那样去算,因为我们要求的是最值,而不是数量,只要确保不遗漏就行了,可以重复。

  1. 最长上升公共子序列问题:实际上也就是最长公共子序列和最长上升子序列的结合,所以我们定义的状态含义f[i,j],是在a序列的前i个中,b序列的前j个中,且以b[j] 结尾的上升公共子序列的长度,属性为最大值。

状态转移方程:状态划分:根据“最后”的原则,因为都要以b[j]结尾嘛,所以简单点的划分就以含不含a[i],为依据,很显然对于不含a[i]就是f[i-1,j]。对于含a[i],又可以划分为很多子集,根据状态的定义,首先满足a[i]==b[j],确保公共,接下来保证上升,想想最长上升子序列的状态划分,即以倒数第二个为依据划分k类,倒数第二个为空,倒数第二个为b[1],倒数第二个为b[k]。而且因为a[i]==b[j],而之后如果是上升的话+1,所以a[i]就不看了,该部分的状态转移方程为对于

首先满足a[i]==b[j].对于 if( b[j ]> b[k] ), f [i , j]=max( f [i, j] , f [i-1, k]+1)(1<=k<j )

优化:朴素版的最长上升公共子序列时间复杂度为n^3,一般动态规划的优化都是在朴素版的基础上进行等价变形达到降低时间复杂度的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值