最长上升子序列(Longest Increasing Subsequence)

原创 2013年12月03日 15:30:51

问题:

给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…<sn并且这个子序列的长度最长。输出这个最长的长度。(为了简化该类问题,我们将诸如最长下降子序列及最长不上升子序列等问题都看成同一个问题,其实仔细思考就会发现,这其实只是<符号定义上的问题,并不影响问题的实质)
例如有一个序列:1 7 3 5 9 4 8,它的最长上升子序列就是 1 3 4 8 长度为4.

分析:
对于几个给定的数字
a1、a2、... an,
必然存在最长上升子路径。有了路径,那么对于路径上的非起始节点ai而言,肯定会存在一个节点是排在ai前面的,不妨记为prev(ai),叫做前驱节点
现在,可以给出问题中的最优子结构了:到达节点ai的最长递增路径,包含了到达这条路径上的每一个节点的最长递增路径。自然地,最长递增路径也就包含了到达前驱节点的最长递增路径。至于证明,可以用反证法简单地做到。
设L(ai)为到达节点ai的最长递增子序列的长度,并且它一般可以定义为
L(ai)=1+max{L(aj), aj<-prev(ai)}。
用文字描述就是:到达节点ai的最长递增子序列的长度,比所有可能的路径上的到达前驱节点aj的子序列的长度中的最大值还要多一。

例子:

原始数据为  5 2 8 6 3 6 9 7 

那么赋予它们的每一个数的初始长度均为0,所以辅助数组的元素全为0并且长度为8。接下来从5出发,对于所有比5大的数,即可以作为5的后续节点,修改为1。那么经过第一轮修改后,辅助数组变成
0 0 1 1 0 1 1 1。
而对于从2开始的情况,在修改时需要注意一点:当发现后续节点所对应的长度已经比先有长度加上一还要大时,就不应该修改了。因为根据最优解的值的递归定义,每一个节点对应的最长子序列的长度在构造时所选取的都是前驱节点中的长度的最大值。所以,第二轮变换后辅助数组变成了
0 0 1 1 1 1 1 1。
变化不大,只是第五个数字3对应的长度变成了1而已。这是对的,因为在3之前,能够作为前驱节点的也只有2而已,所以它们之间的距离是1,加上原来2对应的长度0,还是1。剩下的步骤和之前进行的这两步是一致的,于是到最后,辅助数组就会变成
0 0 1 1 1 2 3 3。
于是我们知道,在数字中拥有最大的长度的,也就是到达它们的递增子序列最长的数字是9和7。那么现在有了两个数组,一个是原来的输入,另一个则是计算出来的辅助数组,通过这两个数组,就可以得到在输入中所存在的至少一个最长递增子序列了。方法也很简单,首先需要找出在辅助数组中具有最大值的元素的下标是几。显然,下标是6和7(从0开始计算的下标),选择一个来演示就可以了,例如6。那么从下标为6的元素位置开始,可以知道它对应的子序列的长度为3,那么它的前驱对应的子序列的长度应该为2,于是将下标不停地减小,直到找到这样的一个数:这个数本身要比测试的数字小,并且它对应的长度恰好比测试的数字对应的长度小一。并且这样一直进行下去,直到找到了的数字对应的长度为0为止。此时把这个最后的数字加入已经得到的序列中,就可以得到在输入中存在的“最长递增子序列”了。

代码:

算法复杂度:O(n^2)

public class LIS {
    
    public static int getLIS(int arr[], int n) {
        int dp[] = new int[n];
        for (int i = 0; i < n; i++) {
            dp[i] = 0;
        }   
        int ans = 0;
        for (int i = 0; i < n-1; i++) {
            ans = dp[i];
            for (int j = i+1; j < n; j++) {
                if (arr[i] < arr[j] && dp[j] < ans+1) {
                    dp[j] = ans+1;
                }                    
            }
        }
        ans = 0;
        for (int i = 0; i < n; i++) {
            if(dp[i] > ans) {
                ans = dp[i];
            }
        }
        return ans;
    }
}
不过,这个方法只是得到最长子序列的长度,并没有实现得到最长子序列。




最长上升子序列LIS(Longest increasing subsequence)

介绍最长上升子序列问题,也就是Longest increasing subsequence缩写为LIS。是指在一个序列中求长度最长的一个上升子序列的问题。问题描述: 给出一个序列a1,a2,a3,...

最长上升子序列LIS(Longest Increasing Subsequence)

最长上升子序列问题是各类信息学竞赛中的常见题型,也常常用来做介绍动态规划算法的引例。问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7....an求它的一个子序列(设为s1,s2,...s...

最长上升子序列 LIS (Longest Increasing Subsequence)

已知一个有N个数的数列a0,a1,...,aN−1a_0, a_1, ..., a_{N-1},求该数列的一个子序列a′0,a′1,...,a′M−1a'_0, a'_1, ..., a'_{M-1}...

最长上升子序列 LIS(Longest Increasing Subsequence)

引出: 问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1 例如有一个序列:1  7  3 ...

最长上升子序列 LIS(Longest Increasing Subsequence)---DP

转载自:http://www.wutianqi.com/?p=1850 引出: 问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn...
  • wconvey
  • wconvey
  • 2012年04月08日 15:50
  • 398

LIS(Longest Increasing Subsequence)最长上升(不下降)子序列

有两种算法复杂度为O(n*logn)和O(n^2) O(n^2)算法分析如下: (a[1]...a[n] 存的都是输入的数) 1、对于a[n]来说,由于它是最后一个数,所以当从a[n]开始查找...

最长上升子序列 Longest Increasing Subsequence n^2和nlogn算法

最长上升子序列 Longest Increasing Subsequence
  • gwq5210
  • gwq5210
  • 2014年10月28日 20:39
  • 565

最长上升子序列 LIS(Longest Increasing Subsequence)(转)

引出:问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1...

[动态规划] 最长递增子序列 (Longest Increasing Subsequence)

1.复杂度为O(n^2) const int maxn=100020; const int inf=0x3f3f3f3f; int dp[maxn];//以a[i]为结尾的最长自增子序列长度 int...

最长递增子序列详解(longest increasing subsequence)

一个各公司都喜欢拿来做面试笔试题的经典动态规划问题,互联网上也有很多文章对该问题进行讨论,但是我觉得对该问题的最关键的地方,这些讨论似乎都解释的不很清楚,让人心中不快,所以自己想彻底的搞一搞这个问题,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:最长上升子序列(Longest Increasing Subsequence)
举报原因:
原因补充:

(最多只允许输入30个字)