最长上升子序列那些事

最长上升子序列太简单啦~~~(dalao这样吐槽)

(遇见神题wa的跟孙子似的……)

  那我们来讲讲LIS的那些事。

  说来也奇怪,今天不知撞上哪位大仙了,遇见模拟赛题总是一眼秒,后来有道题,我还没看完题,说了一句“看着像LIS”,于是就交了一发LIS模板,当场就过了,我自己都被吓到了。。。

  咳咳,我们说正事,LIS对于每个学过dp的同学都不陌生,有O(n^2)做法,过水这里不谈,简单说一说O(nlogn)做法;

  一句话二分!你可能会想,这序列符不符合单调性又不是你说了算的,二分啥?同学你别忘了,近在眼前的题目名称,也就是最长上升子序列,就给了我们一个活生生的单调性。所以,我们准备在这里做做文章。

  首先我们找一个空数组来存放这个单调子序列,要明确,这不一定是最长上升子序列,但是最长上升子序列一定诞生在这个数组里面,我们记录他的长度,在O(n)扫原序列的时候,对于1~n中的每个数i,我们在这个存放数组(以下简称b数组,原序列为a数组)中二分一个位置来插当前这个i,其实并不是插进去,而是替掉某个数,这个数是比i大的最小的那个数,这是贪心的思想,因为替掉那个数等于把那个数变小一点点,他变小了后面上升的空间就更大了,重复这样的操作,b数组最长的长度就是最长上升子序列的长度。

  你可能会异或,既然把数都插到中间去了,b数组就不是LIS了吧,这个你不用担心,因为替换某个数只是贪心,不会引起b数组长度的变化,只有在b数组最右段插数(也就是这个数比当前LIS最大的数都大),才会影响长度,还是不懂可以手玩一下。

  总结一下他的思想,是一个序列的贪心,靠一个数组来存放一个单调的序列,用它来优化复杂度,并用它体现LIS的长度(虽然他并不是LIS),可以说,它是一个等效替代,并且放宽了限制,提供了优化的平台。

有代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=100005;
 4 int a[MAXN],b[MAXN],n,m,k;
 5 int bsearch(int len,int pos){
 6     int l=1,r=len;int mid=(l+r)>>1;
 7     while(l<=r){
 8         if(pos>b[mid]) l=mid+1;
 9         else if(pos<b[mid]) r=mid-1;
10         else return mid;mid=(l+r)>>1;
11     } return l;
12 }
13 int lis(){
14     int len=1;b[1]=a[0];
15     for(int i=1;i<n;i++){
16         int j=bsearch(len,a[i]);
17         b[j]=a[i];if(j>=len) len=j;
18     } return len;
19 }
20 int main(){
21     scanf("%d",&n);
22     for(int i=0;i<n;i++) scanf("%d",&a[i]);
23     cout<<lis()<<endl;return 0;
24 }
LIS

 

 

如果你觉得这个东西只能做裸题,那就是你目光短浅了。

一,有比较裸的题,像《拦截导弹》《怪盗基德的滑翔翼》

 

二,有一道题在openjudge上应该有,叫《登山》,这题要求出起点到每个点的最长上升子序列,要求出每个点到终点的最长下降子序列,然后枚举连接处,对总长取max;

 

三,这道题不好想,CF340D Bubble Sort Graph, 他是怎么转化为LIS的?考虑一下冒泡排序的过程,考虑一下建图的过程,考虑一下独立集的含义,是不是说交换位置是因为逆序,所以逆序就连边,所以不逆序就不连边,所以就独立了!不逆序又是什么意思呢,上升序列一定不逆序吧,那最大独立集,就是最长上升子序列咯!(神奇吧!)

 

四,这就是我说的那个一眼秒,吓到我自己的题 列车调度 因为每次遇到比所有轨道最后一辆车编号都小的车就要新开辟一条轨道,所以可以手模几组数据发现,每条轨道的第一辆车组成了原序列的LIS,所以我们就可以找到正确解法!

hhhhh~(有时候解题需要第六感!!!)

 

那这篇报告到此就结束了,关于一些简单的dp模型,其实说不定有大用,各位一定不要轻敌哦~

 

posted @ 2018-08-19 20:48  杜宇一声  阅读(196)  评论(2)    收藏  举报