• [DP&贪心+二分]LIS最长上升子序列及其相关的问题


    LIS应该是一个比较经典的问题(qwq我昨晚上才弄懂nlogn的算法是怎么做的)

    最长上升子序列指一个序列中,从前往后最长的大小上递增的序列(不需要连续出现但要保证先后顺序与原序列一致)

    比如{1,3,2,5,6,4}中 最长上升子序列可以是{1,2,5,6}

    可以用DP求解一个序列的最长上升子序列

    定义DP[i]为前i个字符的最长子序列长度

    转移方程为:dp[i] = max(dp[j]+1,dp[i])   

    代码:

    int a[maxn],lis[maxn];
    for(int i = 1; i <= ent; i++){
            for(int j = 1; j < i; j++)
                if(a[j] < a[i])
                    lis[i] = max(lis[i],lis[j]+1);
        }

    时间复杂度为O(n^2) 正确性比较显然。 就是对于每一个位置i 遍历其前面的所有字符选取最大的满足条件的位置的值对当前dp值更新

    当然还有更快的owo

    用一个lis数组来维护长度为i的最长上升子序列的最后一个元素,当前最长长度为len

    遍历一遍原序列,对于每个数,如果有a[i] > lis[len] 就把a[i]加到最长序列的末端

    即lis[++len] = a[i]

    否则的话就在lis数组里找到刚好大于等于a[i]的数,将其更新为 a[i](因为我们总是希望当前末尾的数尽可能的小以接上更多的数)

    因为lis数组的性质,它总是降序的,因此我们可以二分查找以降低时间复杂度 

    代码:

    int a[manx],lis[maxn]
    int ansh = 1;
    lis[1] = a[1];
    for(int i = 2; i <= ent; i++){ if(a[i] > lis[ansh]) lis[++ansh] = a[i]; else lis[lower_bound(lis+1,lis+ansh+1,a[i])-lis] = a[i]; }

    其中ansh代表当前最长上升子序列长度

    lower_bound在arr[]到arr[]+size中查找大于等于x的第一个元素,返回其地址

    我们减去lis得到其下标,更新lis数组。

    对于类似的,最长不升子序列、最长下降子序列、最长不降子序列……其思路都是一致的,只需要改改判断条件和lower_bound里的比较器

    (有时要用upper_bound,取决于子序列里允不允许存在相等的元素)

    qwqwqwqwq贴道题:

    洛谷P1020导弹拦截https://www.luogu.com.cn/problem/P1020

    ---------------------------

    回来重温一下…

    总结:贪心+二分的做法

    用f[i]表示长度为i的最长上升子序列的当前最后一个元素

    遍历元素序列,如果当前元素>最长序列的最后一个元素,即可构成更长一点的LIS 即将这个元素插入到f[++len]当中

    如果不大于,则将恰好大于等于他的元素更新为该元素(因为我们总希望各个位置的元素更小, 而LIS不允许有相同元素出现)

  • 相关阅读:
    数组对象去重
    数组对象中key值为数组的数据处理成多个对应的数组对象
    数组对象相同的key值合并,并且把对应的id放到一个数组
    vue使用element-ui tabs切换 实现按需加载
    vue使用element-ui tabs切换echarts 解决宽度100% 问题
    递归获取所有JSON对象
    JS通过内核判断各种浏览器区分360与谷歌
    vue 跳转当前页面不刷新问题
    docker 常用命令
    java 比较时间的几种方法
  • 原文地址:https://www.cnblogs.com/leafsblogowo/p/12665071.html
Copyright © 2020-2023  润新知