首先定义一下LIS问题:给定一个长度为n的序列a,求它的最长上升子序列的最大长度。
方法一:(n^2)。
令f[i]为以i结尾的最长上升子序列的长度,当且仅当j满足a[j]<a[i](1≤j≤i≤n)时,f[i]从f[j]转移而来。
这个状态转移方程易构造:f[j] = MAX(f[i]) + 1(a[j] < a[i]) 。
方法二:(n log n)
现在,我们仔细考虑计算f[i]时的情况。
假设有两个元素a[j1]和a[j2],满足(1)0<j1<j2<i (2)a[j1]<A[j2]<A[i](3)f[j1]=f[j2]。此时,选择f[j1]和选择f[j2]都可以得到同样的f[i]值,那么很明显,选择j1比选j2要好,因为由于条件(2),在a[x+1] ... a[i-1]这一段中,如果存在j3,存在a[j1] < a[j3] < a[j2],则与选择a[j2]相比,将会得到更长的上升子序列。
我们会得到一个启示:根据f[]的值进行分类。对于f[]的每一个取值k,我们只需要保留满足 f[i] = k的所有a[i]中的小值。设g[k]记录这个值,即g[k] = min{a[i] } (f[i] = k)。
注意到g[]的两个特点:
(1)g[k]的值是在整个计算过程中是单调不上升的。
(2)g[]的值是有序的,即g[1] < g[2] < g[3] < ... < g[n]。
利用g[],我们可以得到另外一种计算长上升子序列长度的方法。
设当前已经求出的长上升子序列长度为 len。先判断a[i]与g[len],若a[i] > g[len],则将a[i]接在g[len]后将得到一个更长的上升子序列,len = len + 1,D[len+1] = A[i]?否则,在g[1]..f[len]中,找到大的j,满足f[j] < a[i].令k = j + 1,则有f[j] < a[i] <= f[k],将a[i]接在 f[j]后将得到一个更长的上升子序列,同时更新g[k] = a[i].后,len即为所要求的长上升子序列的长度。由于g[]的特 点(2),我们在D[]中 查找时,可以使用二分查找高效地完成,则整个算法的时间复杂度下降为O(nlogn)。
参考:http://hi.baidu.com/fandywang_jlu/item/da673a3d83e2a65980f1a7e1
题外话:不妨仔细研究一下经典的拦截导弹(贪心加动态规划)
附文(拦截导弹 题意):
一种导弹拦截系统的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统?