LIS学习总结

来源:互联网 发布:易语言取中间文本源码 编辑:程序博客网 时间:2024/06/04 20:15

1.LIS含义:最长递增子序列,可以连续,也可以不连续,只要是最长的就行,注意和连续递增子序列的区别。
2.算法功能:不仅可以求出给定序列的最长递增子序列的长度,而且可以求出给定以序列中任意项结尾的最长递增子序列的长度。

设数列a1,a2,a3,a4,a5........a(n-1),an, (也可以是字符串); a.解法:用动态规划求解主要是动态方程的推导: 1.设dp[i] :a[i]结尾的最长递增子序列的长度; 2.假设a[i]是a[j]的后继,即a[i]>a[j] (j<i) ,那么dp[i]=dp[j]+1,如果对于所有j:1-(i-1a[i]<=a[j] 恒成立,那么dp[i]=1; 3.由2可知:dp[i]=1 if(a[i]>a[j])  dp[i]=max{ dp[j]+1,dp[i] }  (1<=j<i ) 。代码如下:
for(i=1;i<=N;++i){    dp[i]=1;    for(j=1;j<i;++j)    {        if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1);    }}


我们发现上述实现方法时间复杂度太高 对于较大数据不能采用这种方法
优化方案:用二分的思想来优化(必须有序)
步骤:1.设g[i]: 数列的递增子序列长度为 i 时的最小结尾;
2.将a1放入g[1] 代表递增子序列长度为1时的最小结尾是g[1];
3.将a[i] 插入到已经确定最小结尾的序列中 a[i] 的位置下标就代表以a[i]结尾的LIS的长度;
4.直到将a(2-n)都插入到g数组中算法结束,g数组的最大下标就表示a(1-n)的LIS长度。


g数组版:

g[1]=a[1];cnt=2;for(i=2;i<=N;++i){    int iter=lower_bound(g+1,g+cnt,a[i])-g;//左闭右开 严格单增的(用二分还是和函数一起扯)    g[iter]=a[i];    if(iter==cnt) ++cnt; }

vector版:

vector<int> VEC; vector<int>::iterator iter;VEC.push_back(a[1]); //初始化将a[1]放进去 for(i=2;i<=N;++i)//循环插入剩余元素 {    iter=lower_bound(VEC.begin(),VEC.end(),a[i])//查找a[i]应插入的位置    if(iter==VEC.end()) VEC.push_back(a[i]);//如果在区间外直接放入     else *iter=a[i];  //如在区间内 替换 } 

注意:1.在插入的过程之中我们发现g数组是有序的所以采用了二分的方法;
2. lower_boune(begin,end,key) : 返回[begin,end) 地址区间内第一个>=key的地址,用于严格单增;
upper_bound(begin , end , key) : 返回第一个>key的地址, 用于非严格单增;
3.从以上过程中可以看到,我们不仅可以求出LIS的长度,而且可以求出 以任意元素结尾的LIS的长度 (如果需要用STL的容器保存较方便);
4. 结论:给定串的 非严格 ,连续,单调减的子串个数等于该串的 最长,严格单调增的子串的长度(即LIS长度); 证明:数列 a1,a2,a3,a4,a5,a6,a7; 假设此串的LIS(唯一性)是a3,a5,那么其他剩下的元素必然非严格,a1>=a2>a3 < a4 >=a5>=a6>=a7(可推广到一般情况);

5.根据4可以推断: 结论:给定串 非严格,单调增的子串个数等于该串的 最长,严格单减的最长字串的长度(LDS长度);
6.如果题目中含有二维的要求,那么将二维的转化成一维的,然后就可以用LIS的方法了(其实LIS就是题目的尽头,将题目中的条件适当转化就变成特别简单的问题了)。

打印LIS:从头开始逐个插入元素,每次插入只能插入到后面或者替换前一位插入的元素,这样就可以打印LIS了!!

#include<cstdio>#include<vector>#include<algorithm>using namespace std;vector<int> VEC; vector<int>::iterator iter;int a[100];void LIS(int N){    int i=0; VEC.clear();    VEC.push_back(a[1]);    for(i=2;i<=N;++i)    {        iter=lower_bound(VEC.begin(),VEC.end(),a[i]);        if(iter==VEC.end()) VEC.push_back(a[i]);        else if(iter+1==VEC.end()) *iter=a[i];//只更新last就避免i之后的元素插入到i之前    }}int main(){    int T,N,i;    scanf("%d",&T);    while(T--)    {        scanf("%d",&N);        for(i=1;i<=N;++i) scanf("%d",&a[i]);        LIS(N);        printf("%d\n",VEC.size());         for(iter=VEC.begin();iter!=VEC.end();++iter)        {            printf("%d ",*iter);        }        printf("\n");    }    return 0; } 


详细讲解: http://www.cnblogs.com/handsomecui/p/4692350.html

0 0