zoukankan      html  css  js  c++  java
  • 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
    ",len);
        }
    }
  • 相关阅读:
    获取ServletContext方法
    linux下echo命令详解
    非滤波单目视觉SLAM系统研究
    单目深度估计(Monocular Depth Estimation)之摄像机运动对深度估计的影响
    typedef函数指针用法
    REMODE+ORBSLAM运行配置(2) REMODE和编译后的ORB ros工程利用节点实现通讯
    跟我一起学习Makefile
    ubuntu下通过命令打开多个终端并在相应终端执指令
    REMODE+ORBSLAM运行配置(1) 把ORB编译成ROS工程
    ROS节点与运行
  • 原文地址:https://www.cnblogs.com/freinds/p/6400453.html
Copyright © 2011-2022 走看看