HDU 1950 Bridging signals(LIS)

最长上升子序列(LIS)的典型变形,O(n^2)的动归会超时。LIS问题可以优化为nlogn的算法。
定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素。
注意d中元素是单调递增的,下面要用到这个性质。
首先len = 1,d[1] = a[1],然后对a[i]:若a[i]>d[len],那么d[++len] = a[i];
否则,我们要从d[1]到d[len-1]中找到一个j,满足d[j-1]<a[i]<d[j],则根据d的定义,我们需要更新长度为j的上升子序列的最末元素(使之为最小的)即 d[j] = a[i];
最终答案就是len
利用d的单调性,在查找j的时候可以二分查找,从而时间复杂度为nlogn。
==================================

最长上升子序列nlogn算法

最长递增子序列,Longest Increasing Subsequence 下面我们简记为 LIS。

假设存在一个序列a[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5(1,3,4,8,9)。n
下面一步一步试着找出它。
我们定义一个序列d,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把a[1]有序地放到d里,令d[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把a[2]有序地放到d里,因为1<2,就是说长度为1的LIS的最小末尾是1,所以d[1]=2已经没用了,即d[1]=1,很容易理解吧。这时Len=1

然后,a[3] = 5,a[3]>d[1],所以d[1+1]=d[2]=a[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候d[1..2] = 1, 5。这时Len=2

然后,a[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,

长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候d[1..2]=1,3。这时Len=2

第5个,a[5] = 6,它在3后面,因为d[2] = 3, 而6在3后面,于是很容易可以推知d[3] = 6, 这时d[1..3] = 1,3,6。这时Len=3

第6个, a[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到d[3] = 4。d[1..3] = 1,3,4。这时Len=3

第7个, a[7] = 8,它很大,比4大,嗯。于是d[4]=8。这时len=4

第8个, a[8] = 9,得到d[5]=9,嗯。这时len=5

最后一个, a[9] = 7,它在d[3]=4和d[4] = 8之间,所以我们知道,最新的d[4] =7,d[1..5] = 1,3,4,7,9。这时Len=5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个a[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在d中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

#include<cstdio>
#include<algorithm>
using namespace std;
int a[40010],d[40010],len;
int binary_search(int i){
    int l=1,r=len;
    while(l<r){
        int mid=l+(r-l)/2;
        if(d[mid]>=a[i]) r=mid;
        else l=mid+1;
    }
    return l;
}
int main()
{
    int n,p;
    scanf("%d",&n);
    while(n--){
        scanf("%d",&p);
        for(int i=1;i<=p;i++)
            scanf("%d",&a[i]);
        d[1]=a[1],len=1;
        for(int i=2;i<=p;i++){
            if(a[i]>d[len])
                d[++len]=a[i];
            else{//也可以使用库函数lower_bound() 
                int pos=binary_search(i);//int pos=lower_bound(d,d+len,a[i])-d;
                d[pos]=a[i];
            }
        }
        printf("%d\n",len);
    }
}

 

posted @ 2017-02-15 10:48  despair_ghost  阅读(214)  评论(0编辑  收藏  举报