[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

猜你喜欢

转载自www.cnblogs.com/leafsblogowo/p/12665071.html