动态规划之674最长连续递增序列、300最长上升子序列、673最长递增子序列的个数、输出该最长上升子序列

题目:给定一个无序的整数数组,找到其中最长上升子序列的长度。
给定一个未经排序的整数数组,找到最长且连续的的递增序列。
输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
理解:重点把握连续,动态规划.
初始化:dp=[1]*n
转移矩阵为if nums[i]>nums[j]->dp[i]=dp[j]+1。
输出:max(dp)

class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n<=1:
            return n
        dp = [1]*n
        for i in range(1,n):
            if nums[i]>nums[i-1]:
                dp[i] = dp[i-1]+1
        return max(dp)

进阶1:给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

理解:由于题目只要最长上升子序列得长度,并不要求实际子序列。因此可直接利用动态规划。与题一要求不同的是:这个是子序列,而不要求连续。因此需要加一层循环,遍历0-i之间的所有状态。
初始状态为:dp[i] =1。dp[i]代表的是位置i得到的最长上升子序列的长度,因此初始化为1表示只有一个i字符满足条件得情况下则最长上升子串长度即为1。
转移矩阵:dp[i] = max(dp[i], dp[j]+1)。j属于(0,i)。相当于两层遍历,针对0-i之间的位置j, 如果i>j, 则可更新dp[i]=dp[j]+1,当然要针对0-i之间所有的选取最大值作为位置i的状态。.
输出:dp所有位置的最大值即可。

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n==0:
            return 0
        dp =[]
        for i in range(n):
            dp.append(1)
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i], dp[j]+1)
        return max(dp)

当然还有二分+贪心的算法。核心思想为:每次都选取离最大值最近的值进行递增。

进阶2求最长递增子序列的个数。
输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。
理解:相当于求递增子序列的基础之上统计个数,因此直观理解,需要在长度的dp之上增加一个count数组统计当前位置最长子序列的个数,表示当前最长上升子序列可以得到的个数。
初始化:当然都为1
状态转移矩阵:在满足nums[i]>nums[j],即可以递增的情况下进行分类。1)如果dp[i]<=dp[j],即还没有基于状态j进行最长递增子序列的更新:此时更新最长递增子序列dp[i]=dp[j]=1,并且count[i]应等于count[j]。2)如果dp[i]==dp[j]+1,表明此时已经基于j进行子序列更新或者等同长度的更新,如12435中,更新5时,由3的状态得到dp[i]=4,而由4的状态也可以得到dp[i]=4,因此,需要将count[3]+count[4]:那么count[i]+=count[j]。

def findNumberOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n==0:
            return 0
        dp =[]
        count = [1]*n #存储当前i结尾的最长子串的个数
        for i in range(n):
            dp.append(1)
            for j in range(i):
                if nums[i]>nums[j]:#可以上升
                    #dp[i] = max(dp[i], dp[j]+1)
                    #新增判断
                    if dp[i] <= dp[j]:#还未上升过
                        dp[i] = dp[j] +1
                        count[i] = count[j]#由j上升的得到的i,自己个数等同于j的个数
                    elif dp[i] == dp[j]+1:#如12435中,5可以由3得到,也可以由4得到。
                        count[i] += count[j]
        max_length = max(dp)
        return sum(c for i, c in enumerate(count) if dp[i] == max_length)

进阶3输出该最长上升子序列。
思路:利用回溯法,基于最长上升子序列的输出dp[i], 及其位置。然后从后往前找dp[i]-1的位置加入到res中.

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        n = len(nums)
        if n==0:
            return 0
        dp =[]
        pos = 1
        for i in range(n):
            dp.append(1)
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i], dp[j]+1)
                    pos = i#找出最大的那个位置,从后往前找
        deepth = max(dp)#输出最大的值
        res = []
        for j in range(pos, -1, -1):
            if dp[j] = deepth:
                res.insert(0, nums[j])
                deepth -=1#最长上升子序列的长度值依次减一往前找。
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值