最大上升子序列算法

寻找a[1]..a[n]的最大上升子序列时,可以用的dp[]来记录最大的子序列长度,有状态转移方程:
              
dp[i] = max{dp[j]}+1, 1<=j<i,a[j]<a[i]

这个算法的时间复杂度O(n^2),代码如下:

int LTS(int *a,int *dp,int len){
    int max=0;
   for(int i=0;i<=len;i++){
        dp[i]=1;
     for(int j=0;j<i;j++)
        if(a[j]<a[i])
        dp[i]=dp[i]>(dp[j]+1)?dp[i]:(dp[j]+1);
     max=max>dp[i]?max:dp[i];
     rerutn max;
   }

现在重点看下算法复杂度为O(nlogn)算法。

现在我们用一个数组d[],使得d[len]=a[k],数组下标用来表示上升子序列的长度,数组值代表序列a[k]的值(可知dp[k]=len)。

假设有d[k]=a[i],则当再读入a[t],当a[t]>a[i]时,d[k+1]=a[t];

但是一般情况下a[t]会大于很多个数,比如说d[k-s]=a[j](a[j]<a[j]),如果我们选取了a[j]进行比较最长上升子序列就少了s个!!那么该跟哪个数进行比较呢?我们可以按照以下步骤。

 初始化使d[1]=a[1],当读入a[t]时,对比a[t]与d[max] 
(  d[max]=a[k],max表示目前的最大上升子序列的长度
):
  1)a[t]>a[k]  则d[max+1]=a[t];
 
2)否则,在d中利用二分查找找到比a[t]小的最大数d[i],使d[i+1]=a[t]。

第二步是很为关键的。现在我们可以看一个实例就知道为什么这么做。
  求数列 1,5,2,3
的最大上升子序列。用这个算法执行过程如下:

d[1]=1;

a[2]>d[1],故d[2]=a[2]=5;
      
a[3]<d[2],在d中二分查找找到第一个比a[3]小的d[1],故d[1+1]=a[3]=2,d[2]=2;
       a[4]>d[2],故d[2+1]=a[4]=3;

可以得到最大上升子序列为3!
 
原本的d[2]为5后来更新为2,如果不进行更新那么当读入a[4]=3时,就漏掉了一个!这其实是个策略:如果数列中a[x]和a[y],

1)x<y

2)a[x]>a[y]

3)dp[x]=dp[y],

当再读入a[t]要选择时,到底取哪一个构成最优的呢?
   
显然是选择a[y],因为后面读入的a[t],如果a[x]>a[t]>a[y],那么选择a[x]就会漏掉,而选择a[y]就可以防止漏掉。也就是说我们对于当前读入的数列,都要使:

d[len]=min{ a[i] | dp[i]=
len }

在d中利用二分查找找到比a[t]小的最大数d[i],代码如下:

//二叉搜索
int SearchBin(int a[],int key,int len){
    int low=1,high=len;
    while(low<=high){
        int mid=(low+high)/2;
        if(a[mid]<key&&a[mid+1]>key) return mid;
        else if(a[mid]<key) low=mid+1;
        else high=mid-1;
    }
    return 0;
}
int LTS(int *a,int *d,int len){
    d[1]=a[1];
    int max_len=1;
    for(int i=2;i<=len;i++){
        if(a[i]>d[max_len]){
            d[++max_len]=a[i];
        }
        else{
            int t=SearchBin(d,a[i],max_len);
            d[t+1]=a[i];
        }
    }
    return max_len;
}
时间: 2024-07-31 14:29:25

最大上升子序列算法的相关文章

最长上升子序列算法(n^2 及 nlogn) (LIS)

问题描述: 一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的.对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N.比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等.这些子序列中最长的长度是4,比如子序列(1, 3, 5

最长上升子序列算法

这题目是经典的DP题目,也可叫作LIS(Longest Increasing Subsequence)最长上升子序列 或者 最长不下降子序列.很基础的题目,有两种算法,复杂度分别为O(n*logn)和O(n^2) . A.O(n^2)算法分析如下: (a[1]...a[n] 存的都是输入的数) 1.对于a[n]来说,由于它是最后一个数,所以当从a[n]开始查找时,只存在长度为1的上升子序列: 2.若从a[n-1]开始查找,则存在下面的两种可能性:    (1)若a[n-1] < a[n] 则存在

算法重拾之路——最长公共子序列(LCS)

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 第二章:动态规划 最长公共子序列 算法描述: 一个给定序列的子序列是该序列中删去若干元素后得到的序列.确切的说,若给定序列 X={ x1,x2,...,xm },则另一序列 Z = { z1,z2, ... ,zk },是X的子序列是指存在一个严格递增下标序列

算法期末考试练习题

一.选择题 1.算法分析中,记号O表示(B),记号?标售(A),记号Θ表示(D) A 渐进下界 B 渐进上界 C 非紧上界 D 紧渐进界 E 非紧下界 2.以下关于渐进记号的性质是正确的有:(A) A  f(n) =Θ(g(n)),g(n) =Θ(h(n)) ⇒f(n) =Θ(h(n)) B  f(n) =O(g(n)),g(n) =O(h(n)) ⇒h(n) =O(f(n)) C  O(f(n))+O(g(n)) = O(min{f(n),g(n)}) D  f(n) = O(g(n)) ⇔g

最长递增子序列

问题 给定一个长度为N的数组,找出一个最长的单调自增子序列(不一定连续,但是顺序不能乱).例如:给定一个长度为6的数组A{5, 6, 7, 1, 2, 8},则其最长的单调递增子序列为{5,6,7,8},长度为4. 解法1:最长公共子序列法 这个问题可以转换为最长公共子序列问题.如例子中的数组A{5,6, 7, 1, 2, 8},则我们排序该数组得到数组A‘{1, 2, 5, 6, 7, 8},然后找出数组A和A’的最长公共子序列即可.显然这里最长公共子序列为{5, 6, 7, 8},也就是原数

clojure实现最长上升子序队列算法

4Clojure上的一道题:4Clojure 最长上升子序列算法 描述如下: Given a vector of integers, find the longest consecutive sub-sequence of increasing numbers. If two sub-sequences have the same length, use the one that occurs first. An increasing sub-sequence must have a lengt

基础dp C Monkey and Banana (类最长上升子序列)

题目大意:有一个猴儿和一些种类的箱子,每种类箱子有各自的长宽高,数目有无限个.猴子想把箱子尽可能的堆积起来, 堆积的条件通俗的讲就是放在下面的箱子能够撑得住上面的箱子,按数学建模来说就是放在下面的箱子的长和宽要比放在上 面的要大(严格大于):由于每种箱子虽然数量是无限的,但是肯定不能同种箱子直接累积,因为那样底面的边长就相等了, 这样就是不合法的了,所以每种箱子可以转换成六种情况的不同箱子(分别作为长宽高),之后就是个最长满足条件的高度序 列就行了,即套用最长上升子序列算法思想即可. #incl

Git中的merge命令实现和工作方式

想象一下有如下情形:代码库中存在两个分支,并且每个分支都进行了修改,最后你想要将其中的一个分支合并到其他的分支中.个人博客网址 http://swinghu.github.com/ 那么要问合并的处理过程是怎么样的呢?Git是对每个分支,依据分支的历史数据按照序列化操作,还是它只是合并每个分支里文件的最后版本?这是一个问题,我想对git的merge操作有必要进行分析一下. 回忆一下,我们知道Git的版本库内部结构是以有向无环图(directed acyclic graph)组织起来的:每一次co

文件比较

1.按行将文本文件内容打印到屏幕 任务描述: 按行读取文本文件内容并输出到新的文件中 命令行中以文件名做输入参数 相关知识: FILE *fopen(const char *path, const char *mode):打开一个文件,返回指向FILE结构体的指针 ssize_t getline(char **lineptr, size_t *n, FILE *stream):从文件流中读取一行字符串,并将字符串的地址存储到lineptr指向的指针中.返回的字符串以空字符’\0’结尾,同时包含换