理解最长上升子序列 ( LIS ) 的O(nlogn)算法看我就够啦~

网上对于这个算法的讲解排名靠前的都是一派胡言, 自己没理解就写博客,于是我决定写一篇通俗易懂的讲解。

我们知道LIS的状态转移方程是dp[ i ] = ( 0 <= j < i , a[ j ] < a[ i ] )max{ dp[ j ] + 1}

边界条件为dp[ 0 ]  = 0;

我们维护一个数组d[ ] ,其中d[ i ] 表示长度为 i 的LIS的最小结尾值。

由d的定义可知,d数组内元素是递增的。

从1到n循环,对于每个数a[ i ], 如果a[ i ]比d中最大的数d[ len ]大,说明a[ i ]可以接在d[ len ]这个数后面变成长度+1的上升子序列,而d中元素个数++len代表目前的LIS长度。

反之,则从d中二分找到第一个大于a[ i ]的数, 记录其位置为p, 则在p之前的数都比a[ i ]小,而a[ i ]比d[ p ]小,也就是说a[ i ]可以接在任意长度比p小的序列后面,即可以接在长度为p-1的序列后面,即可以替换d[ p ]。

代码:

const int maxx = 1e5 + 5;
int a[maxx];
int d[maxx];
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        int len = 0;
        d[++len] = a[1];
        for(int i = 2; i <= n; ++i)
        {
            //不严格上升用upper_bound
            int p = lower_bound(d + 1, d + 1 + len, a[i]) - d;
            if(p == len + 1)
                d[++len] = a[i];
            else
                d[p] = a[i];
        }
        printf("%d\n", len);
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值