最长上升子序列问题 nlogn 实现算法的简述

首先举个例子说明最长上升子序列(longest increasing subsequence 缩写 LIS):

  1,4,6,2,3,7,5 中1,2,3,5 和1,4,6,7都是最长上升子序列,长度均为4,且相邻元素不能相等。

LIS是动态规划中的经典问题,O(n2)的做法是设d(i)为以i为结尾的最长上升子序列的长度,状态转移方程为:d[i]=max{0,d[j]|j<i,A[j]<A[i]}+1。

下面我们仔细思考以下情况:

  i<j时,d[i]=d[j],显然这种情况只能是A[i]>=A[j];这时我们计算 d[t](t>j且A[t]>A[i]),那么应优先选取以A[j]结尾的子序列作为A[t]的前缀序列,因为如果存在

i<j<z<t,满足A[j]<A[z]<A[i]<A[t],子序列的长度会因z的存在而增加。

  由此我们使用数组D[k]保存满足d[t]=k的最小A[t],即D[k]=min{A[t]|d[t]=k};

  可以证明D[k]是严格单调递增的,即D[1]<D[2]<D[3]<……<D[len],

证明如下:

  D[k]=min{A[t1]|d[t1]=k};

  D[k+1]=min{A[t2]|d[t2]=k+1};

  采用反证法,

  令A[t1]=D[k],A[t2]=D[k+1]

  假设A[t2]<A[t1];

  设以A[t2]结尾对应的子序列为S[1]~S[k],A[t2],  满足S[k]<A[t2].

  显然S[1]~S[k]是一个以S[k]为结尾的最长上升子序列,长度为k,

  则有A[t1]=D[k]<=S[k]<A[t2],与假设矛盾,故D为严格单调递增序列。

于是利用D我们可以得到另外一种计算最长上升子序列的方法,并且可以边读边计算D,算法如下:

  1)设当前最大子序列长度为len,读入A[i];

  2)如果A[i]>D[len],则len++,D[len]=A[i];

  3)如果A[i]<=D[len],则从1~len中二分查找第一个k,使D[k]>=A[i],更新D[k]=A[i].

代码如下:

 1   int n;//原序列长度
 2     cin>>n;
 3     memset(A, 0,sizeof A);
 4     memset(D, 0, sizeof D);
 5     int len=0;//当前最长子序列长度
 6     for(int i=0;i<n;i++){
 7         cin>>A[i];
 8         if(A[i]>D[len]){
 9             len++;
10             D[len]=A[i];
11         }
12         else {
13             int k=lower_bound(D, D+len, A[i])-D;//二分搜索D[k]>=A[i],更新D[k]
14             D[k]=A[i];
15         }
16     }
17     cout<<len<<endl;
18     return 0;
时间: 2025-01-04 03:05:54

最长上升子序列问题 nlogn 实现算法的简述的相关文章

最长递增子序列 O(NlogN)算法

https://www.felix021.com/blog/read.php?entryid=1587&page=3&part=1 感谢作者! 标题:最长递增子序列 O(NlogN)算法 出处:Blog of Felix021 时间:Wed, 13 May 2009 04:15:10 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?1587  内容: 今天回顾WOJ1398,发现了这个当时没有理解透彻的算法. 看了好久

最长上升子序列 O(nlogn)解法 (转)

转自 http://www.cppblog.com/mysileng/archive/2012/11/30/195841.html 最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i]. 这样简单的复杂度为O(n^2),其实还有更

最长递减子序列(nlogn)(个人模版)

最长递减子序列(nlogn): 1 int find(int n,int key) 2 { 3 int left=0; 4 int right=n; 5 while(left<=right) 6 { 7 int mid=(left+right)/2; 8 if(res[mid]>key) 9 { 10 left=mid+1; 11 } 12 else 13 { 14 right=mid-1; 15 } 16 } 17 return left; 18 } 19 20 int Lis(int a[

LCS(最长公共子序列)动规算法正确性证明

今天在看代码源文件求diff的原理的时候看到了LCS算法.这个算法应该不陌生,动规的经典算法.具体算法做啥了我就不说了,不知道的可以直接看<算法导论>动态规划那一章.既然看到了就想回忆下,当想到算法正确性的时候,发现这个算法的正确性证明并不好做.于是想了一段时间,里面有几个细节很trick,容易陷进去.想了几轮,现在把证明贴出来,有异议的可以留言一起交流. 先把一些符号和约定说明下: 假设有两个数组,A和B.A[i]为A的第i个元素,A(i)为有A的第一个元素到第i个元素所组成的前缀.m(i,

(转载)最长递增子序列 O(NlogN)算法

原博文:传送门 最长递增子序列(Longest Increasing Subsequence) 下面我们简记为 LIS. 定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素.注意d中元素是单调递增的,下面要用到这个性质.首先len = 1,d[1] = a[1],然后对a[i]:若a[i]>d[len],那么len++,d[len] = a[i];否则,我们要从d[1]到d[len-1]中找到一个j,满足d[j-1]<a[i]<d[j],

最长上升子序列O(nlogn)算法详解

最长上升子序列 时间限制: 10 Sec   内存限制:128 MB 题目描述 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.我们想知道此时最长上升子序列长度是多少? 输入 第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N) 输出 1行,表示最长上升子序列的长度是多少. 样例输入 3 0 0 2 样例输出 2 提示 100%的数据 n&l

最长上升子序列O(NlogN)算法

题目 给出一个长度为N的序列,请求出其最长上升子序列的长度. 题解 分析 首先很容易想到O(N2)的算法 f[i]表示1~i最长上升子序列长度则 f[i]=max{f[j]}+1,(1≤j<i)∧(a[j]<a[i]) 显然我们需要的只是满足(1≤j<i)∧(a[j]<a[i])这一条件的最大f[j],那么我们为何不将他记录下来呢 用g[i]表示f[j]=i 时的 a[j]min len表示当前最大上升自序列长度 易证: g[k]在程序运行过程中不会变得更大 (1≤k≤len) g

51nod 1134 最长递增子序列 (O(nlogn)算法)

1134 最长递增子序列 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递增的) 例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10. Input 第1行:1个数N,N为序列的长度(2 <= N <= 50000) 第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9) Output 输

P3402 最长公共子序列(nlogn)

P3402 最长公共子序列 题目背景 DJL为了避免成为一只咸鱼,来找Johann学习怎么求最长公共子序列. 题目描述 经过长时间的摸索和练习,DJL终于学会了怎么求LCS.Johann感觉DJL孺子可教,就给他布置了一个课后作业: 给定两个长度分别为n和m的序列,序列中的每个元素都是正整数.保证每个序列中的各个元素互不相同.求这两个序列的最长公共子序列的长度. DJL最讨厌重复劳动,所以不想做那些做过的题.于是他找你来帮他做作业. 输入输出格式 输入格式: 第一行两个整数n和m,表示两个数列的