网上对于这个算法的讲解排名靠前的都是一派胡言, 自己没理解就写博客,于是我决定写一篇通俗易懂的讲解。
我们知道LIS的状态转移方程是dp[ i ] = ( 0 <= j < i , a[ j ] < a[ i ] )max{ dp[ j ] + 1}
边界条件为dp[ 0 ] = 0;
我们维护一个数组d[ ] ,其中d[ i ] 表示长度为 i 的LIS的最小结尾值。
由d的定义可知,d数组内元素是递增的。
从1到n循环,对于每个数a[ i ], 如果a[ i ]比d中最大的数d[ len ]大,说明a[ i ]可以接在d[ len ]这个数后面变成长度+1的上升子序列,而d中元素个数++len代表目前的LIS长度。
反之,则从d中二分找到第一个大于a[ i ]的数, 记录其位置为p, 则在p之前的数都比a[ i ]小,而a[ i ]比d[ p ]小,也就是说a[ i ]可以接在任意长度比p小的序列后面,即可以接在长度为p-1的序列后面,即可以替换d[ p ]。
代码:
const int maxx = 1e5 + 5;
int a[maxx];
int d[maxx];
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
int len = 0;
d[++len] = a[1];
for(int i = 2; i <= n; ++i)
{
//不严格上升用upper_bound
int p = lower_bound(d + 1, d + 1 + len, a[i]) - d;
if(p == len + 1)
d[++len] = a[i];
else
d[p] = a[i];
}
printf("%d\n", len);
}
return 0;
}