问题:数组a,长度n,求a的最长子序列长度
状态定义:f[i] 定义为元素a[i]结尾的最长上升子序列长度,答案为max(f),初始化f[0] = 0,f[i] = 1
状态转移:从0 <= j < i的范围内找a[j] < a[i] 的最大f[j] + 1来更新f[i]
f = [1]*n
for i in range(n):
for j in range(i):
if a[j] < a[i]:
f[i] = max(f[i],f[j] + 1)
print(max(f))
LIS除了用DP,还可以用贪心+二分来求
g = []
for x in a:
idx = bisect_left(g,x)
if idx == len(g):
g.append(idx)
else:
g[idx] = x
print(len(g))
题目1:怪盗基德的滑翔翼
# 由于可以往两个方向飞,所以要求两个方向的LIS长度取最大
def LIS(a):
# 以i结尾的最长上升子序列
f = [1]*n
for i in range(n):
for j in range(i):
if a[j] < a[i]:
f[i] = max(f[i],f[j] + 1)
return max(f)
for _ in range(int(input())):
n = int(input())
a = list(map(int,input().split()))
print(max(LIS(a),LIS(a[::-1])))
input()
a = list(map(int,input().split()))
n = len(a)
# 最长上升子序列
f = [1]*n
for i in range(n):
for j in range(i):
if a[j] < a[i]:
f[i] = max(f[i],f[j] + 1)
# 最长下降子序列
g = [1]*n
for i in range(n - 1,-1,-1):
for j in range(n - 1,i,-1):
if a[i] > a[j]:
g[i] = max(g[i],g[j] + 1)
print(max(a + b - 1 for a,b in zip(f,g)))
# 这题需要一点思考,如何抽象成LIS问题,要求两岸友好城市连线不相交,我们把一边按照编号从小到大排序,#然后求这些编号对应的友好城市编号的最长上升子序列即可,这样求得的友好城市连线就不会相交了
n = int(input())
a = []
for _ in range(n):
a.append(tuple(map(int,input().split())))
a.sort()
f = [1]*n
for i in range(n):
for j in range(i):
if a[i][1] > a[j][1]:
f[i] = max(f[i],f[j] + 1)
print(max(f))
n = int(input())
a = list(map(int,input().split()))
f = a[:]
for i in range(n):
for j in range(i):
if a[j] < a[i]:
f[i] = max(f[i],f[j] + a[i])
print(max(f))
# 每一发导弹的轨迹都是一个最长非上升子序列,求一发导弹最多能拦截多少个,相当于求最长非上升子序列
import sys
RI = lambda: map(int, sys.stdin.buffer.readline().split())
RS = lambda: map(bytes.decode, sys.stdin.buffer.readline().strip().split())
RILST = lambda: list(RI())
a = RILST()
n = len(a)
f = [1]*n
for i in range(n):
for j in range(i):
if a[j] >= a[i]:
f[i] = max(f[i],f[j] + 1)
print(max(f))
# 求最少用多少个非上升子序列可以覆盖所有导弹
# 389 207 155 300 299 170 158 65
# 389一组导弹,最后高度155
# 300来了之后没法让155更小,只能再发射一颗,此时数组g=[155,300]
# 299-158都可以接在300后面,g=[155,158]
# 65可以接在155后面,g = [65,158]
# 所以最后只需要两个导弹,回顾这个过程,和贪心法求最长上升子序列是一样的
# 65和158,分别是两个最长非递增子序列的末尾元素
# [389 300 299 170 158 65] [207 155]
# 贪心求最长上升子序列长度,最后g里就是每个最长上升子序列的末尾元素
from bisect import bisect_left,bisect_right
g = []
for x in a:
idx = bisect_left(g,x)
if idx == len(g):
g.append(x)
else:
g[idx] = x
print(len(g))
# 这题如果导弹高度是严格下降的,那g应该是求最长非下降子序列长度,用bisect_right
from bisect import bisect_right,bisect_left
while True:
n = int(input())
if n == 0:
break
else:
a = list(map(int,input().split()))
n = len(a)
up,down = [0]*n,[0]*n # 初始值不重要,我们用jk下标控制了数组范围
ans = n
def dfs(i,j,k):
global ans
if j + k >= ans:
return
if i == n:
ans = j + k
return
# 找第一个小于a[i]的位置,放在上升子序列后
idx = 0
while idx < j and up[idx] >= a[i]: # 因为只能拦截更高的,所以低的导弹来了就得发射新的来拦截,数组+1
#up里每个元素都是一个递增子序列的最后一个元素,如果当前元素大于最后一个元素,直接把最后一个元素变成当前元素,数组长度不会增加
idx += 1
t = up[idx]
up[idx] = a[i]
if idx < j:
dfs(i + 1,j,k)
else:
dfs(i + 1,j + 1,k)
up[idx] = t
# 放在下降子序列之后
# down里每个元素代表一个下降子序列的最后一个元素
# 如果当前元素小于某个down元素,代表可以接在这个元素的序列后
idx = 0
while idx < k and down[idx] <= a[i]:
idx += 1
# 找第一个大于a[i]的元素
#idx = bisect_right(down,a[i],hi = k)
t = down[idx]
down[idx] = a[i]
if idx < k:
dfs(i + 1,j,k)
else:
dfs(i + 1,j,k + 1)
down[idx] = t
dfs(0,0,0)
print(ans)
n = int(input())
from math import inf
# 最长公共上升子序列
# a,b是长分别为m,n的两个数组,找出a,b中公共上升子序列的长度
# dp定义:f[i][j]为a的前i个数、b的前j个数中以b[j]结尾的(可以包含a[j],也可以不包含)最长上升子序列的长度
# 最终答案为max(f[m][j] for j in range(1,n+ 1))
a = [-inf] + list(map(int,input().split()))
b = [-inf] + list(map(int,input().split()))
m,n = len(a) - 1,len(b) - 1
f = [[0]*(n + 1) for _ in range(m + 1)]
for i in range(1,m + 1):
maxv = 0
for j in range(1,n + 1):
# 如果不选a[i]
f[i][j] = f[i - 1][j]
# 如果选a[i],那么a[i] == b[j]
if a[i] == b[j]:
f[i][j] = max(f[i][j],1 + maxv)
if a[i] > b[j]: # a[i]可以作为b[j]之后的b[k]匹配
maxv = max(maxv, f[i - 1][j])
print(max(f[m][j] for j in range(1,n + 1)))