前面我们已经简单的介绍了LIS,并学习了复杂度为O(n*n)的算法,但是因为它的复杂度有点高,所以有的时候会超时,在这里我们再学习一种复杂度为O(NlogN)的算法:
O(NlogN)做法:贪心+二分
我们还是举一个例子来描述一下这个过程,我们有一个序列a[]={1,7,3,5,9,4,8},再定义一个数组dp[];用Len来代表此时的LIS;
利用贪心的思想,对于一个上升子序列,显然当前最后一个元素越小,越有利于添加新的元素,这样LIS长度自然更长。
我们的dp就是用来存此时选中的数字,
在这里我们让a数组从1开始,
首先我们先来看a[1]。因为只有一个数字,所以我们把a[1]添加到dp中,此时dp[]={1};此时len=1;
然后看a[2],因为a[2]>a[1],所以此时我们把a[2]也添加到dp中,此时dp[]={1,7},len=2;
然后我们看a[3],我们从dp中找到那么第一个大于a[3]的数,如果找到,那么我们就把这个数字替换成a[3],此时dp[]={1,3},len还是等于2;如果找到不到呢,那么证明此时这个数字就是最大的,我们把它也添加到dp数组中,再把len=len+1;
再看a[4],我们再dp中找不到比a[4]大的数字,所以此时dp[]={1,3,5},len=len+1.
......................................
这就是大致的过程吧,但是有一个问题很容易错,那就是最后dp数组存的数并不是LIS的路径,至于为什么?在这里先不做解释,以为我也不会,哈哈;
在找dp中的比a[i]打的数中,其实我们没有必要一个一个的去找,我们可以用二分法,这个方相信大家都会吧。
如果让你用二分法你会怎么用?你是不是自己写一个函数,然后去用,其实没有必要这么麻烦,我们·可以直接调用一个函数
lower_bound(dp,dp+n,m)
这个函数里面有三个参数,第一个和第二个就是你要从哪找到哪,后面的m就是你要找的那个数字,
这个函数得返回值就是比m>=的那个数的位置·,也就相当于返回一个指针吧,
下面给出LIS O(NlogN)的模板:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e3+7;
const int MAX=0x3f3f3f3f;
int a[maxn];
int main()
{
memset(a,MAX,sizeof(a));//先把数组的初值赋为最大。
int n;
scanf("%d",&n);
int A;
for(int i=1;i<=n;i++)
{
scanf("%d",&A);
*lower_bound(a,a+n,A)=A;//用二分法
}
printf("%d\n",lower_bound(a,a+n,MAX)-a);
}