最长上升子序列(LIS):
对于给定的一个序列(a1, a2, …, aN),我们也可以从中得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N,其中最长的子序列就是最长上升子序列,本文求最长上升子序列的长度。
举例:
序列:1 2 7 3 5 4
可得到的子序列:(1 2 7) (1 2 3 5) (1 2 3 4) (2 7) (2 3 5) (2 3 4) (7) (3 5) (3 4) (5) (4)
则该序列的最长上升子序列的长度为4,有两个分别为(1 2 3 5) (1 2 3 4)
LIS长度的求解方法有三种:
1.O(n^2)的DP
2.O(nlogn)的二分+贪心法
3.O(nlogn)的树状数组优化的DP
第一种:O(n^2)的DP
动态规划的解法,思路为:当求n长序列LIS的长度时,先求n-1长序列LIS的长度,再与第n个数比较。求前n-1个数的最长上升子序列,可以通过求前n-2个数的最长上升子序列……直到求前1个数的最长上升子序列,此时LIS当然为1。
假设a数组为序列数组,a[ i ]表示序列中下标 i 对应的数;f 数组用来保存子序列长度,f[ i ]表示以a[ i ]作为最后一位数的子序列的长度。
将a[ i ]与前i-1位数比较,如果a[ i ]大于之前的某个数a[ j ],则f[ j ]+1,再与f[ i ]比较,取最大值。
例如:1 2 7 3 5 5
前1个数,a[1] = 1 f[1] = 1 子序列为1
前2个数,a[2] = 2 1 < 2 f[2] = f[1] + 1 = 2 子序列为1 2
前3个数,a[3] = 7 2 < 7 f[3] = f[2] + 1 = 3 子序列为1 2 7
前4个数,a[4] = 3 2 < 3 < 7 f[4] = f[2] + 1 = 3 子序列为1 2 3
前5个数,a[5] = 5 3 < 5 < 7 f[5] = f[4] + 1 = 4 子序列为1 2 3 5
前6个数,a[6] = 5 5 = 5 f[6] = f[5] = 4 子序列为1 2 3 5
max(f)即为所求LIS长度
n = int(input()) a = list(map(int,input().split())) f = [1]*n for i in range(n): # i代表当前dp的数 for j in range(i): # j代表[0,i-1]区间的数 if a[i] > a[j]: f[i] = max(f[i],f[j]+1) elif a[i] == a[j]: f[i] = max(f[i],f[j]) else: pass ans = max(f) print(ans)
第二种:O(nlogn)的二分+贪心法
假设a数组为序列数组,a[ i ]表示序列中下标 i 对应的数;b数组用来保存相同长度子序列最后一位最小值,b[ i ]表示 i 长的所有子序列最后一位的最小值。对于一个上升子序列,显然其结尾元素越小,越有利于在后面接其他的元素,也就越可能变得更长。
因此,我们只需要维护 b 数组,对于每一个a[ i ],如果a[ i ] > b [当前最长的LIS长度],就把 a[ i ]接到当前最长的LIS后面,即b [当前最长的LIS长度 + 1] = a [ i ]。 如果等于就跳过。如果小于,就用二分法从b数组中找到第一个大于等于a [ i ]的元素b[ j ],用a [ i ]去更新b[ j ]。
例如:1 2 7 3 5 5
a[1] = 1 b[1] = 1 b为1
a[2] = 2 1 < 2 b[2] = 2 b为1 2
a[3] = 7 2 < 7 b[3] = 7 b为1 2 7
a[4] = 3 2 < 3 < 7 b[3] = 7 b为1 2 3
a[5] = 5 3 < 5 < 7 b[4] = 5 b为1 2 3 5
a[6] = 5 5 = 5 continue b为1 2 3 5
b[-1]即为所求
import sys b = [sys.maxsize]*(n+1) # [1,n] def binary_search(lis,lis_effective_len,p): # 返回比a[i]大或等于的数的下标 l,r = 1,lis_effective_len while r >= l: # [8,9] 8 --> 8 == 8 , r - 1 ,[8] --> 8 == 8 , r - 1 , l = r + 1 , lis[l] = 8 mid = (l + r)//2 if lis[mid] >= p: r = mid - 1 else: l = mid + 1 return l b[1] = a[0] ans = 1 for i in range(n): if a[i] > b[ans]: ans += 1 b[ans] = a[i] elif a[i] == b[ans]: continue else: b[binary_search(b,ans,a[i])] = a[i] print(ans)
使用bisect库(二分库),bisect_left(ls, x)返回大于等于x的下标
from bisect import bisect_left b = [a[0]] for i in range(n): if a[i] > b[-1]: b.append(a[i]) elif a[i] == b[-1]: continue else: b[bisect_left(b,a[i])] = a[i] print(len(b))
第三种:O(nlogn)的树状数组优化的DP