Python3使用动态规划处理最长上升子序列问题:NC163最长上升子序列(一)【附视频教程】


建议新手白脖子先看视频教程: 【小鱼老师算法题】python讲解动态规划思想的最长递增子序列问题

题目介绍

  • 原题链接:NC163 最长上升子序列(一)
  • 描述
    给定一个长度为 n 的数组 arr,求它的最长严格上升子序列的长度
    所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列
    我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标i和j满足 i<ji<j 且 a r r i ≥ a r r j arr_i \geq arr_j arriarrj
  • 数据范围: 0 ≤ n ≤ 1000 0\leq n \leq 1000 0n1000
  • 要求:时间复杂度 O ( n 2 ) O(n^2) O(n2), 空间复杂度 O ( n ) O(n) O(n)
  • 示例1
    输入:
    [6,3,1,5,2,3,7]
    
    返回值:
    4
    
  • 说明:该数组最长上升子序列为 [1,2,3,7] ,长度为4

题解1:往回遍历

建议新手白脖子先看视频教程:【小鱼老师算法题】python讲解动态规划思想的最长递增子序列问题
另外我的题解是为了方便在本地IDE进行调试,所以没有新建类,而是使用exec函数,不太熟悉的可以参考:Python3使用exec函数将输入进来的结果的字符串的值解包成变量的值

li = []
exec('li=' + input())
dp = [1] * len(li)
for i in range(1, len(li)):
    for j in range(i):
        if li[i] > li[j]:
            dp[i] = max(dp[i], dp[j]+1)
print(max(dp) if li else 0)

在这里插入图片描述

  • 知识点:动态规划
    动态规划算法的基本思想是:将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。动态规划算法将问题的解决方案视为一系列决策的结果
  • 思路:
    要找到最长的递增子序列长度,每当我们找到一个位置,它是继续递增的子序列还是不是,它选择前面哪一处接着才能达到最长的递增子序列,这类有状态转移的问题常用方法是动态规划
  • 具体做法:
    step 1:用dp[i]表示到元素i结尾时,最长的子序列的长度,初始化为1,因为只有数组有元素,至少有一个算是递增
    step 2:第一层遍历数组每个位置,得到n个长度的子数组
    step 3:第二层遍历相应子数组求对应到元素i结尾时的最长递增序列长度,期间维护最大值
    step 4:对于每一个到iii结尾的子数组,如果遍历过程中遇到元素j小于结尾元素,说明以该元素结尾的子序列加上子数组末尾元素也是严格递增的,因此转移方程为 d p [ i ] = d p [ j ] + 1 dp[i]=dp[j]+1 dp[i]=dp[j]+1
  • 图示:
    请添加图片描述

题解2:二分查找

dp[i]表示从左至右到原序列第i个元素的最长递增子序列的长度,从第i个元素往回遍历更新dp[i]的值。由于每个元素都需要往回遍历一次,时间复杂度是 o ( n 2 ) o(n^2) o(n2)。往回遍历如何更新dp[i]的值在题解1中已有很好的介绍,这里主要写用二分法代替往回遍历的过程,时间复杂度是 o ( n l o g ( n ) ) o(nlog(n)) o(nlog(n))

二分法的过程为:首先创建数组arr=[ele_1],ele_1是原序列第一个元素,然后从第二个元素开始从左至右遍历原序列

  1. 如果ele_i > max(arr),将ele_i加到arr最后
  2. 如果ele_i <= max(arr),用二分法找到arr中第一个比ele_i大(或相等)的元素并用ele_i替换

遍历完成后arr的长度即为最长递增子序列的长度(但arr不是最长递增子序列)。第二步替换是因为遍历到的元素可能会有比ele_i大但比替换元素小的元素,比如原序列为[2,5,8,3,4,6]。

import bisect

li = []
exec('li=' + input())
if not li:
    print(0)
    exit()
dp = [1] * len(li)
sorted_list = [li[0]]
for i in range(1, len(li)):
    if li[i] > sorted_list[-1]:
        sorted_list.append(li[i])
        dp[i] = len(sorted_list)
    else:
        position = bisect.bisect_left(sorted_list, li[i])
        sorted_list[position] = li[i]
        dp[i] = position+1
print(max(dp))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

COCO56(徐可可)

建议微信红包:xucoco56

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

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

打赏作者

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

抵扣说明:

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

余额充值