分析
最长上升子序列:对于一个序列A[],找出一个子序列x[], 使xi < xj(i < j),xi
和xj不一定连续,求最长的一个子序列
O(n^2)做法:
定义状态:d[i]为当前元素作为最后一个元素的LIS长度
转移方程:d[i] = max{d[j] + 1 | i > j && a[i] > a[j]};
目标:max(d[i])
状态:O(n), 转移O(n),时间复杂度O(n ^ 2)
O(nlgn)做法;
思路:对于具有相同d[i]的两个元素a和b,并且a < b,那么每次只保留a一定不会丢失最优解(假设只保留了a, 设下一次选择的元素使x,如果满足x > a, 那么LIS里面可新加入x。但是若只保留了b, 设下一次选择的元素为x,且满足x > a,但是x < b,这时候LIS里面不会加入x,并且此时对于x,得到的不是以x为最后一个元素的LIS)
因此对于具有相同d[i]的任意两个元素a和b,我们只保留小的那个元素。设g[i]保存的是当前d值为i的最小元素值。那么每次新插入一个元素x,只需要二分查找该元素可插入在g[]中的位置,并且更新g[]数组即可
时间复杂度O(nlgn)
代码
//初始化g[],g[]下标从1开始计数,因为LIS的最小长度为1
for (int i = 1; i <= n; i++) g[i] = INF;
for (int i = 0; i < n; i++) {
//找到当前元素A[i]可在g数组中插入的位置
//二分查找,时间复杂度O(lgn)
int k = lower_bound(g + 1, g + 1 + n, A[i]) - g;
//更新g[]
g[k] = A[i];
//更新d[]
d[i] = k;
}