最长上升子序列优化算法 【算法作业】

题目:

    设计一个O(n²)时间的算法,找出由n个数组成的序列的最长单调递增子序列。

思路分析:


    O(n²)算法的复杂度很大一部分浪费在查找前i-1项的最长上升子序列上。如果我们能直接保存前i项的最大上升子序列,那么当我们处理第i项时只需要调用即可。

      如果同样长的子序列,当然它的尾部元素越小越好。所以设s[i]为长度为i的子序列尾部元素的最小值。我们可以知道s[i]序列是单调递增的,因为如果s[i]>s[i+1],那么取s[i+1]中序列的前i个元素代替s[i],所得的s’[i]<s[i],这和s[i]的定义矛盾。所以这里我们可以知道s[i]序列是单调递增的。

      对于第i个数a[i],我们为了维护s[]数组,找到其对应的恰好大于a[i]的一个数,用a[i]去代替它。为什么这样就可以维护s[]数组?因为当如果a[i]>s[k-1]a[i]<=s[k],那么a[i]至少可以接到长度为k-1的序列之后,得到长度为k的序列,又因为原来长度为k的序列的最小值比a[i]小,所以用a[i]去替代它。

      最后输出s[]序列能达到的最大长度即可。

      因为s[i]序列是单调递增的,那么我们可以用二分的方法去查找,复杂度为O(logn)。再考虑遍历n个元素,所以总的时间复杂度为O(nlogn)


代码:

#include<bits/stdc++.h>
#define N 1100
using namespace std;
int len,n,a[N],s[N];

int _find(int t)
{
    int l=1,r=len;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(s[mid]>=t)    r=mid;
            else    l=mid+1;
    }
    return l;
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        s[len=1]=a[1];
        for(int i=2;i<=n;i++)
            if(a[i]>s[len])
                s[++len]=a[i];
            else
            {
                int pos=_find(a[i]);
                s[pos]=a[i];
            }
        cout<<"maxlen="<<len<<endl;
    }
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值