📅  最后修改于: 2023-12-03 15:40:17.442000             🧑  作者: Mango
最长递增子序列(Longest Increasing Subsequence,LIS)指的是在一个数字序列中,选取其中的若干个数字组成一个最长的严格递增子序列。
例如,序列1 3 5 4 7 6 8中,最长递增子序列是1 3 5 7 8,长度为5。
本文将介绍构建LIS的动态规划算法,以及打印LIS的方法。
设序列a的长度为n,考虑构建一个长度为n的数组dp,其中dp[i]表示以a[i]结尾的最长递增子序列长度。
显然,dp[i]的值是由dp[j]和a[i]决定的,其中j < i且a[j] < a[i],也就是说,如果在a[1]到a[i-1]中存在元素a[j],使得a[j] < a[i],那么dp[i]的值就取dp[j]+1这个最大值。
最终的LIS长度为dp数组中的最大值。
具体实现时,可以使用两个for循环,外层循环遍历a数组,内层循环遍历a数组中i之前的所有元素j,计算dp[i]的值。
def LIS(a):
n = len(a)
dp = [1] * n # 初始化dp数组为1
for i in range(n):
for j in range(i):
if a[j] < a[i]:
dp[i] = max(dp[i], dp[j]+1) # 更新dp[i]的值
return max(dp) # 返回LIS长度
a = [1, 3, 5, 4, 7, 6, 8]
print(LIS(a)) # 输出5
上面的代码实现了LIS的构建,但并没有打印出LIS序列,接下来我们将介绍如何打印LIS序列。
在构建LIS的过程中,我们不仅要计算LIS的长度,还要记录LIS的路径,也就是最长递增子序列包含哪些元素。
可以定义一个数组pre,pre[i]表示以a[i]结尾的最长递增子序列中,a[i]前面的那个元素的位置。
在计算dp[i]时,如果存在多个dp[j]+1等于dp[i]的j值,我们选择pre[j]最小的那个j值,以保证得到的LIS是字典序最小的。
最后,可以倒序遍历pre数组,从LIS的最后一个元素开始,一直找到第一个元素,构建出LIS序列。
以下是完整代码:
def LIS(a):
n = len(a)
dp = [1] * n # 初始化dp数组为1
pre = [-1] * n # 初始化pre数组为-1
for i in range(n):
for j in range(i):
if a[j] < a[i] and dp[j] + 1 > dp[i]:
dp[i] = dp[j] + 1 # 更新dp[i]的值
pre[i] = j # 记录路径,j是i的前一个元素
lis_len = max(dp) # LIS长度
lis = []
idx = dp.index(lis_len) # 找到LIS的最后一个元素的位置
while idx != -1:
lis.append(a[idx])
idx = pre[idx] # 找到下一个元素的位置
lis.reverse() # 将LIS反转,得到正常顺序的LIS序列
return lis_len, lis # 返回LIS长度和LIS序列
a = [1, 3, 5, 4, 7, 6, 8]
lis_len, lis = LIS(a)
print(lis_len) # 输出5
print(lis) # 输出[1, 3, 5, 7, 8]
本文介绍了LIS的构建和打印LIS序列的动态规划算法,并提供了完整的Python代码示例。
LIS是一个经典的动态规划问题,在实际应用中十分常见,例如任务调度、最长公共子序列等领域。掌握动态规划算法和LIS问题解决方法对于程序员而言是非常重要的。