最长上升子序列(Longest Increasing Subsequence,LIS)(动态规划与二分查找结合)

代码

时间复杂度为 O(N log N)

import bisect

n = int(input())
a = list(map(int, input().split()))
tails = []  
for item in a:
    pos = bisect.bisect_left(tails, item)
    if pos == len(tails):
        tails.append(item)
    else:
        tails[pos] = item

print(len(tails))

测试用例如下:

输入

6
1 4 2 2 5 6

输出

4

代码分析

这段代码旨在找到给定整数序列中的最长上升子序列(Longest Increasing Subsequence,LIS)。这种求解方法结合了动态规划思路和二分查找的高效性。

  1. 导入 bisect 模块

    • bisect 模块提供了对有序数组的二分查找功能,使得所需插入位置的查找能够在 O(log N) 的时间复杂度内完成。
  2. 读取输入

    • 从输入中读取一个整数 n,它表示序列的长度。
    • 然后读取 n 个整数,将它们存储在一个列表 a 中,这些整数可以代表任何需要处理的数字,例如战力值、学生成绩等。
  3. 初始化 tails 列表

    • tails 是一个用于存储当前找到的上升子序列尾部元素的列表。每个位置的元素都保持着非严格递增的顺序。这个数组的长度即为最长上升子序列的长度。
  4. 寻找最长上升子序列

    • 对于列表 a 中的每一个元素 item
      • 使用 bisect.bisect_left(tails, item) 来查找 itemtails 中应该插入的位置 pos,这个位置是 tails 中第一个大于或等于 item 的索引。
      • 如果 pos 等于 tails 的长度,这意味着 item 大于 tails 中的任何元素,因此可以将 item 直接追加到 tails 中。
      • 如果 pos 小于 tails 的长度,说明 item 可以替代该位置的元素,这样做的目的是为了保持 tails 中的值尽可能小,以便为下一个可能的上升子序列保留空间。
  5. 输出结果

    • 最终,打印 tails 的长度,表示找到的最长上升子序列的长度。

理论

此算法的核心在于利用 tails 列表动态地维护当前可以构成的上升子序列的尾部。通过二分查找方法能够快速定位插入位置,从而保证在 O(log N) 的时间复杂度内完成对 tails 的更新。整体的逻辑简单而高效,相较于传统 O(N^2) 的动态规划方法,显著提高了运行效率,特别适合于处理大规模输入数据。

时间复杂度

  • 时间复杂度:O(N log N)
    • 主要由两部分组成:遍历每个输入元素(N)和在每次迭代中执行的二分查找(O(log N))。

空间复杂度

  • 空间复杂度:O(N)
    • 最坏情况下,tails 中可能包含与输入长度相同的元素(即输入序列完全递增时),因此其空间复杂度为 O(N)。其他用来辅助存储的变量占用常数空间。

示例

假设输入的是一组数字,例如 [5, 3, 4, 8, 6, 7]。使用该代码计算最长上升子序列的长度:

  1. 初始化tails 列表为空。
  2. 输入处理
    • 对于 5tails 变成 [5]
    • 对于 3tails 变为 [3](替换 5)。
    • 对于 4tails 变为 [3, 4]
    • 对于 8tails 变为 [3, 4, 8]
    • 对于 6tails 变为 [3, 4, 6](替换 8)。
    • 对于 7tails 变为 [3, 4, 6, 7]
  3. 输出:最终 tails 的长度为 4,表示存在的最长上升子序列为 [3, 4, 6, 7]

因此,这段代码高效地解决了最长上升子序列的问题,适合在面对大数据量时使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新时代先锋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值