最长上升子序列问题 nlogn 实现算法的简述
Posted kiraa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长上升子序列问题 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;
以上是关于最长上升子序列问题 nlogn 实现算法的简述的主要内容,如果未能解决你的问题,请参考以下文章