最长上升子序列(LIS)

LIS就是给定n个数,a1,a2....an,按照从左到右的顺序,选出一些数,组成一个上升子序列 ,例如1 6 2 3 7 5,可以选出1 2 3 5,也可以1 6 7,但是前面的要更长一些

dp[i]表示以ai结尾的最长的上升子序列的长度,那么dp[i]={max(dp[j])+1,j<i&&num[j]<num[i]},根据这个转移方程,我们就可以写出程序

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;

int num[maxn],dp[maxn];

int main(){
    int n;
    scanf("%d",&n);
    //输入
    for(int i=0;i<n;i++)
        scanf("%d",num+i);
    //处理
    int ans=1;
    dp[0]=0;
    for(int i=1;i<n;i++){
        int p=1;
        for(int j=0;j<i;j++)
            if(num[j]<num[i])
                p=max(dp[j],p);
        ans=max(ans,dp[i]=p+1);
    }
    printf("%d
",ans);
    return 0;
}
View Code

上面的程序的复杂度是O(n^2),其实还可以优化为O(n log n),方法如下

我们要求dp[i]的时候是要得到所有j<i&&num[j]<num[i]的最大的dp[j],dp[i]这里我们表示的是以第i个数结尾的时候最长的上升子序列,如果我们用dp[i]表示上升子序列长度为i

的最小值,我们可以发现,dp这个数组是一个严格递增的序列,这样对于每个数我们就可以用二分的方法求出以这个数结尾的上升子序列是多长,所有的dp都初始化为INF,INF只要比

输入的数的最大值大就行了,很简单,实现见代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int INF=1e8+5;
int num[maxn],dp[maxn];

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",num+i);

    dp[1]=num[1];dp[n+1]=INF;
    for(int i=2;i<=n;i++){
        dp[i]=INF;
        int t=lower_bound(dp+1,dp+1+i,num[i])-dp;
        dp[t]=min(dp[t],num[i]);
    }
    int ans=lower_bound(dp+1,dp+2+n,INF)-dp-1;
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/jihe/p/5211905.html