最长上升子序列

描述

一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
你的任务,就是对于给定的序列,求出最长上升子序列的长度.

输入

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

输出

最长上升子序列的长度。

样例

  • Input
    7
    1 7 3 5 9 4 8
  • Output
    4

解法

  • 法一:普通dp。用dp[i]以a[i]为结尾的最长上升子序列,则状态转移方程为:
    dp[i]=max(dp[i],dp[j]+1)(0<=j < i,a[i]>a[j])
    时间复杂度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    #include<bits/stdc++.h>
    #define INIT(a,b) memset(a,b,sizeof(a))
    #define LL long long int
    using namespace std;
    const int MAX=0x7fffffff;
    const int MIN=-0x7fffffff;
    const int INF=0x3f3f3f3f;
    const int Mod=1e9+7;
    const int MaxN=1e7+7;
    int main(){
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
    	int n,a[1005],dp[1005];
    	cin>>n;
    	for(int i=0;i<n;i++){
            cin>>a[i];
            dp[i]=1;
    	}
    	int ans=1;
    	for(int i=1;i<n;i++)
            for(int j=0;j<i;j++)
                if(a[i]>a[j]){
                    dp[i]=max(dp[j]+1,dp[i]);
                    ans=max(dp[i],ans);
                }
        cout<<ans<<endl;
        return 0;
    }
    
  • 法二:dp+二分。用dp[i]表示长为i的上升串中最小的末尾数,用len记录当前上升子序列的最大长度,当处理a[i]时,先比较a[i]和dp[len]的大小,如果a[i]>dp[len],则dp[++len]=a[i];如果a[i]<=dp[len],则在dp序列中找到第一个比a[i]大的最小末位数并用a[i]替换掉。时间复杂度.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    #include<bits/stdc++.h>
    #define INIT(a,b) memset(a,b,sizeof(a))
    #define LL long long int
    using namespace std;
    const int MAX=0x7fffffff;
    const int MIN=-0x7fffffff;
    const int INF=0x3f3f3f3f;
    const int Mod=1e9+7;
    const int MaxN=1e7+7;
    int N,len,a[1005];
    int dp[1005];//dp[i]表示长为i的上升串中最小的末尾数
    int main(){
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
    	cin>>N;
    	for(int i=1;i<=N;i++){
            cin>>a[i];
            dp[i]=INF;
    	}
        dp[1]=a[1],len=1;
        for(int i=1;i<=N;i++){
            if(a[i]>dp[len])dp[++len]=a[i];
            else *upper_bound(dp+1,dp+N+1,a[i])=a[i];//找到第一个比a[i]大的最小末尾数并替换
        }
    	cout<<len<<endl;
        return 0;
    }
    
  • 法三:dp+树状数组。在的DP中,dp[i]=max(dp[j])+1(0<=j < i,a[i]>a[j])。现在我们用树状数组来维护dp[j]的最大值(a[i]>a[j]),这样的话查找max(dp[j])的复杂度就从O(n)降到了O(lgn).但是我们在用树状数组处理之前,对a数组进行排序(注意先保存各个数的位置)。时间复杂度O(nlgn)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
    #include<bits/stdc++.h>
    #define INIT(a,b) memset(a,b,sizeof(a))
    #define LL long long int
    using namespace std;
    const int MAX=0x7fffffff;
    const int MIN=-0x7fffffff;
    const int INF=0x3f3f3f3f;
    const int Mod=1e9+7;
    const int MaxN=1e7+7;
    int dp[1005],N;
    struct Num{
        int val,key;
        bool operator < (Num& e)const{return val==e.val?key>e.key:val<e.val;}
        //数值相同序号大的放前面,防止相等
    }num[1005];
    int lowbit(int x){return x&-x;}
    void update(int x,int New){
        for(;x<=N;x+=lowbit(x))
            dp[x]=max(dp[x],New);
    }
    int que(int x){
        int res=MIN;
        for(;x;x-=lowbit(x))
           res=max(res,dp[x]);
        return res;
    }
    int main(){
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        cin>>N;
        for(int i=1;i<=N;i++){
            cin>>num[i].val;
            num[i].key=i;
        }
        sort(num+1,num+N+1);
        int ans=MIN;
        for(int i=1;i<=N;i++){
            int Max=que(num[i].key);//找到编号小于num[i].key的最长上升子序列大小
            update(num[i].key,++Max);//长度+1,然后更新后面,因为num有序,所以保证能够接在后面
        }
        for(int i=N;i>0;i-=lowbit(i))
            ans=max(ans,dp[i]);
        cout<<ans<<endl;
        return 0;
    }
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值