• LIS的O(nlogn)算法


    出自蓝书《算法竞赛入门经典训练指南》

    求最长上升子序列是很常见的可以用动态规划解决的问题……

    很容易根据最优子结构之类的东西得出

    $ ext{dp}[i]$为以第i个数结尾的最长上升子序列长度

    定义$max{emptyset}=0$,粗略地写出

    [ ext{dp}[i] = max left{ ext{dp}[j]|0leqslant j < i,A[j] < A[i] ight} + 1]

    状态数$mathcal{O}({n})$,如果直接枚举转移,转移数$mathcal{O}({n})$,时间复杂度$mathcal{O}({n^2})$

    现在想办法加速转移……

    设$ ext{dp}^{-1}[x]$为$x= ext{dp}[i]$中$ ext{A}[i]$最小的$i$

    设$ ext{pd}[x]= ext{A}[ ext{dp}^{-1}[x]]$

    若有$ ext{A}[i]< ext{A}[j]$且$ ext{dp}[i]== ext{dp}[j]$,那么之后的元素只需要比$ ext{A}[i]= ext{pd}[x]= ext{pd}[ ext{dp}[i]]$大就可以用$ ext{dp}[i]$进行转移

    很容易得[ ext{pd}[1]leqslant ext{pd}[2]leqslant ext{pd}[3]leqslant cdots leqslant ext{pd}[n] ag{1}label{1} ]

    [ ext{dp}[i]=maxleft{x|0leqslant j < i, ext{pd}[x]<A[i] ight}+1]

    即最大的小于A[i]的下标加1,也就等价于最小的大于等于A[i]的下标,设为$k$ $ ag{2}label{2}$

    因为最后$[l,r)$区间收缩到$emptyset$时左侧区间最后一个元素加一就是右侧区间第一个元素

    用STL的lower_bound就不需要自己写二分了

    因为$eqref{1}eqref{2}$,所以$A[i]leqslant ext{pd}[k]$,转移以后需要更新$ ext{pd}[k]$

    但是之前少考虑了$0leqslant j < i$,只需将未计算的$ ext{pd}[x]$设为INF就好了= =

    时间复杂度$mathcal{O}(nlog n)$

    代码

    REPE(i,1,n) pd[i]=INF;
    REP(i,0,n) {
        int k=lower_bound(pd+1,pd+1+n,A[i])-g;
        dp[i]=k;
        pd[k]=A[i];
    }
    

    很容易得最长非降子序列只需将lower_bound改为upper_bound(同样照着二分的参考图)

    然后最长下降子序列只需添加greater<int>()参数,并且初始化为-INF

    注意dp为以第i个数结尾的长度,所以求最长还需求一遍max

  • 相关阅读:
    mysql的统计函数(聚合函数)
    mysql中的五子查询
    mysql-蠕虫复制--快速插入数据
    mysql 外键的使用
    我的mysql入门笔记
    阿里云官方 Centos7 源码安装 LAMP环境
    xml格式数据转excel
    mysql的安装配置
    sublime中,怎么把所有的函数代码都折叠起来?
    点击文字或按钮弹出一个DIV窗口(DIV悬浮窗口)
  • 原文地址:https://www.cnblogs.com/sahdsg/p/10622662.html
Copyright © 2020-2023  润新知