最长单调递增子序列LIS
2015-01-16 11:21
190 查看
问题:
给出一个长度为n的数组,求出最长单调递增子序列(不一定连续,但顺序不能乱)。如 数组 {3 ,1,5,2,7 },结果为 {3,5,7}或{1,5,7},长度为3。
解法 一:最长公共子序列
将原数组按升序排序,然后将得到数组与原数组求最长公共子序列,其结果就是最长递增子序列(以下简称LIS)。
如 3 ,1,5,2,7 排序后得到1,2,3,5,7,其最长公共子序列3,5,7或1,5,7就是所求结果。
解法二:动态规划 o(n^2)
首先找出描述问题的状态。假设要求数组A
的LIS,设d[i]为以A[i]结尾的LIS,那么只要枚举队列中的每一个元素作为序列尾,那么max(d(i),0<=i<N)就是整个数组LIS的长度。那么如何求出每个d(i)呢?以d(i)为状态,那么容易想出它的状态转移方程:d(i)=max(1,d(j)+1),j<i且A[j]<=A[i]。
代码如下:
解法三:o(n*lg(n))算法
如何达到n*lg(n)呢?这里的一个n肯定是扫描数组的一重循环,那么在循环内要怎么处理才能是lg(n)呢? 还是假设要求数组A
的LIS,这里再用一个辅助数组d
,d[i]表示长度为i的LIS结尾元素的最小值(因为长度为i的LIS可能有多个,这里只存结尾元素最小的那个),接下来的具体过程如下:
假设A[1...9]={2,1,5,3,6,4,8,9,7 },接着是构造d
数组:
1:将A[1]=2有序插入d
中,此时d
为空,直接放入d[1]的位置,此时d[1]=2,代表以2结尾的序列长度len=1;
2:将A[2]=1,有序插入d
中,因为d[1]=2,所以舍弃2,将1放入d[1]的位置,此时d[1]=1,代表以1结尾的序列长度len=1;
3:将A[3]=5,有序插入d
中,因为d[1]=1<5,而d[1]后没有元素了,所以将5放入d[2]的位置,此时d[2]=5,代表以5结尾的序列长度len=2;
4:接着是插入A[4],因为d[1]<A[4]=3<d[2],所以将3放入d[2]的位置,原来的5就舍弃不要了,此时d[2]=3,代表以3结尾的序列长度len=2;
5:再插入A[5],因为A[5]=6>d[2],所以将6放在d[2]的后面,此时d[3]=6;
6:接着是A[6],由d[2]<A[6]=4<d[3],所以用4覆盖原本的d[3],此时d[3]=4;
7:接着是A[7],由A[7]=8>d[3],放在d[3]的后面,此时d[4]=8;
8:同7,有d[5]=A[8]=9;
9:由于d[3]<A[9]=7<d[4],用7覆盖d[4],此时d[4]=7;
至此,数组A[1...9]已经扫描完了,而数组d[]的长度为4,于是A[1...9]的LIS长度为4。但这里要注意,d[]数组里的序列并不是LIS。
那么为什么d[i]存的要是长度为i的序列中结尾元素最小的那个呢?因为d[i]越小,在i以后就越可能构造出更长的递增序列。
还可以看出,将A[]中元素插入d[]里面时,由于d[]是有序的,因此可以用二分插入,这样时间就降到了 lg(n)!!!
代码如下:
给出一个长度为n的数组,求出最长单调递增子序列(不一定连续,但顺序不能乱)。如 数组 {3 ,1,5,2,7 },结果为 {3,5,7}或{1,5,7},长度为3。
解法 一:最长公共子序列
将原数组按升序排序,然后将得到数组与原数组求最长公共子序列,其结果就是最长递增子序列(以下简称LIS)。
如 3 ,1,5,2,7 排序后得到1,2,3,5,7,其最长公共子序列3,5,7或1,5,7就是所求结果。
解法二:动态规划 o(n^2)
首先找出描述问题的状态。假设要求数组A
的LIS,设d[i]为以A[i]结尾的LIS,那么只要枚举队列中的每一个元素作为序列尾,那么max(d(i),0<=i<N)就是整个数组LIS的长度。那么如何求出每个d(i)呢?以d(i)为状态,那么容易想出它的状态转移方程:d(i)=max(1,d(j)+1),j<i且A[j]<=A[i]。
代码如下:
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> using namespace std; const int N=10010; int n,a ,d ; int main() { int i,j; while(~scanf("%d",&n)) { for (i=0;i<n;d[i]=1, cin>>a[i++]); int ans=0; for (i=0;i<n;i++){ for (j=0;j<i;j++){ if (a[i]>a[j]) d[i]=max(d[i],d[j]+1); } ans=max(ans,d[i]); } cout<<ans<<endl; } }
解法三:o(n*lg(n))算法
如何达到n*lg(n)呢?这里的一个n肯定是扫描数组的一重循环,那么在循环内要怎么处理才能是lg(n)呢? 还是假设要求数组A
的LIS,这里再用一个辅助数组d
,d[i]表示长度为i的LIS结尾元素的最小值(因为长度为i的LIS可能有多个,这里只存结尾元素最小的那个),接下来的具体过程如下:
假设A[1...9]={2,1,5,3,6,4,8,9,7 },接着是构造d
数组:
1:将A[1]=2有序插入d
中,此时d
为空,直接放入d[1]的位置,此时d[1]=2,代表以2结尾的序列长度len=1;
2:将A[2]=1,有序插入d
中,因为d[1]=2,所以舍弃2,将1放入d[1]的位置,此时d[1]=1,代表以1结尾的序列长度len=1;
3:将A[3]=5,有序插入d
中,因为d[1]=1<5,而d[1]后没有元素了,所以将5放入d[2]的位置,此时d[2]=5,代表以5结尾的序列长度len=2;
4:接着是插入A[4],因为d[1]<A[4]=3<d[2],所以将3放入d[2]的位置,原来的5就舍弃不要了,此时d[2]=3,代表以3结尾的序列长度len=2;
5:再插入A[5],因为A[5]=6>d[2],所以将6放在d[2]的后面,此时d[3]=6;
6:接着是A[6],由d[2]<A[6]=4<d[3],所以用4覆盖原本的d[3],此时d[3]=4;
7:接着是A[7],由A[7]=8>d[3],放在d[3]的后面,此时d[4]=8;
8:同7,有d[5]=A[8]=9;
9:由于d[3]<A[9]=7<d[4],用7覆盖d[4],此时d[4]=7;
至此,数组A[1...9]已经扫描完了,而数组d[]的长度为4,于是A[1...9]的LIS长度为4。但这里要注意,d[]数组里的序列并不是LIS。
那么为什么d[i]存的要是长度为i的序列中结尾元素最小的那个呢?因为d[i]越小,在i以后就越可能构造出更长的递增序列。
还可以看出,将A[]中元素插入d[]里面时,由于d[]是有序的,因此可以用二分插入,这样时间就降到了 lg(n)!!!
代码如下:
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> using namespace std; const int N=10010; int n,a ,d ; int binFind(int l,int r, int x) { int i,j,m; if (d[r]<x) return r+1; while(l<=r){ m=(l+r)>>1; if (d[m]<x) l=m+1; else if (d[m]>x) r=m-1; else return m; } return l; } int main() { int i,j; while(~scanf("%d",&n)) { int t,l=0,r=0; cin>>t; d[r]=t; for (i=1;i<n;i++){ cin>>t; j=binFind(l,r,t); d[j]=t; r=max(j,r); } cout<<r+1<<endl; } }
相关文章推荐
- [dp]最长单调递增子序列LIS
- 最长递增子序列LIS再谈
- 最长单调递增子序列
- 时间复杂度为O(nlogn)的最长单调递增子序列
- 最长单调子序列LIS的简单动态规划算法
- POJ 3903 Stock Exchange_LIS(最长递增子序列) 经典例题!
- 编程之美---求数组中最长递增子序列LIS
- 玲珑杯 1097题 LIS(最长递增子序列)
- 算法设计作业LIS(最长递增子序列)
- nyoj 17 数据结构 最长单调递增子序列
- 设计一个O(n2)时间的算法,找出由n个数组成的序列的最长单调递增子序列。
- 求最长单调递增子序列
- 动态规划4:LIS最长递增子序列问题
- HDU 1080-Super Jumping! Jumping! Jumping!(LIS最长上升子序列-最大递增子段和)
- LIS(最长递增子序列) Zigzag
- 最长递增子序列LIS的O(nlogn)的求法
- (DP6.1.4.1)UVA 111 History Grading(最长递增子序列LIS 的LCS 解法)
- LIS(最长递增子序列) Zigzag
- 最长递增子序列LIS
- LIS 求最长递增子序列