LIS学习总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值