码迷,mamicode.com
首页 > 其他好文 > 详细

LIS问题——n*logn解法

时间:2015-03-07 22:51:54      阅读:454      评论:0      收藏:0      [点我收藏+]

标签:

简述:
LIS问题,即最长上升子序列问题,经典的解法有序列DP,通过这个算法,可以获得最长上升子序列的各种详细信息。但是,我们有时候只需要求最长上升子序列的长度,但是o(n^2)的时间复杂度太慢了,我们希望有一种算法,可以更快一点。

算法过程
既然动态规划太慢了,那么自然就想到了贪心。下述算法,就是运用了二分+贪心
首先考虑一个序列a:1、3、2、7、5、6、4 (一共7个数),另外一个辅助数组d,最终序列长度为ans
第1个数:d[1] = a[1] = 1 , ans = 1 ;
第2个数:a[2] > a[1] , 所以d[2] = a[2] , ans = 2 ;
第3个数:d[1] < a[3] < d[2] , 此时ans不改变,但是根据贪心法则,d[2] = a[3] ;
第4个数:a[4] > d[2], d[3] = a[4] = 7 , ans = 3 ;
第5个数:a[5] < d[3] , ans不变,根据贪心法则,d[3] = a[5] ;
第6个数:a[6] > d[4] , d[4] = a[6] , ans =4 ;
第7个数:a[7] < d[4] , 为保证算法完整性,d[3] = a[7] (这里替代的数是第一个大于key的数)。
此时ans=4,d数组为:1、2、4、6 。
虽然我们算出了答案,但是我们并不能得到最终的最长子序列,这就是这个算法的不足。

代码:
用poj 2533来测代码,题目链接:poj 2533

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int n,ans,a[1005],d[1005];

int upper(int key)          //二分找到第一个比key大的数
{
    int low=1,high=ans,mid;
    if(d[ans]<key)
        return ans+1;
    while(low<high)
    {
        mid=(low+high)/2;
        if(d[mid]<key)
            low=mid+1;
        else
            high=mid;
    }
    return high;
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        d[1]=a[1],ans=1;
        for(int i=2;i<=n;i++)
        {
            int pos=upper(a[i]);
            d[pos]=a[i];
            ans=max(ans,pos);
        }
        printf("%d\n",ans);
    }

    return 0;
}

总结:
运用二分的思想,虽然达不到找到最长子序列的效果,但是找到答案绰绰有余,所以这个算法还是有它的价值的。

LIS问题——n*logn解法

标签:

原文地址:http://blog.csdn.net/fuyukai/article/details/44119471

(2)
(5)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!