leetcode 300 最长上升子序列

https://leetcode-cn.com/problems/longest-increasing-subsequence/

在这里插入图片描述
注意子序列不要求连续
一般这种最长啊,最大啊,不出意外就是dp了
根据之前的选择情况来更新当前的选择情况
一开始对着样例想了一个贪心
既然是求最长的子序列,那么我假设当前第一个子串就是数组的开头,并且使用一个变量来存储最长上升子序列的最后一个元素,记为R,一开始第一个R就是数组的开头
然后往后找一个最小的元素,这是分为两种情况

  1. 如果后面的最小元素大于当前的R,那么放心的加入子序列,同时更新R和最大长度,然后从新加入的元素的下标继续寻找最小的元素
  2. 如果后面的最小元素小于等于当前的R,那么当前子序列废弃,更新目前获得的最大长度,然后将这个最小元素设为新的子序列开头
    对着这个样例很美好
    [10,9,2,5,3,7,101,18]
    直接找到2 3 7 18,长度为4
    但是这种就不行了
    [8,9,10,4]
    直接从8跳到4了

dp

然后我就屈服于DP了,自己真的不会写DP(还是写得太少了),看了一眼题解
说一下思路
dp[i]表示以i下标结尾数组,其中最长的上升子序列长度
假设j<i,表示j下标位于i下标之前
那么dp[i]的选择就依赖于之前的最大值了,也就是max(dp[j]),0<=j<i
但是由于子序列要求上升,所以nums[i]必须要大于nums[j],这个时候dp[i]才能设置成max(dp[j])+1
但是当nums[i]<nums[j]的时候,并不是直接把dp[i]设置成1,因为它有可能继承其它最长上升子序列的长度
举个例子

nums:4	7	8	6
dp	:1	2	3	2->(4,6)

所以我们的状态转移方程中的max不是标准库函数里面的max,而是要遍历之前的dp[j]才能找到尽可能的最大值
dp推完了那就是写代码了…难点就是怎么把状态转移方程推出来

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        # 最直观的想法:
        if not nums:
            return 0
        maxsize=1
        dp=[1]*len(nums)
        for i in range(1,len(nums)):
            for j in range(0,i):
                if nums[j] < nums[i]:
                    dp[i]=max(dp[i],dp[j]+1)
        return max(dp)

DP就是辣么🐂🍺,但是自己写不出

贪心+二分

这里先证明了一个最长上升子序列的性质
在这里插入图片描述
概括一下,我们要求的最长上升子序列要尽可能地长,意为序列的元素要尽可能地多
怎么才能尽可能地多呢
首先子序列肯定是有序的,有序肯定可以用二分
每当我们遍历到一个新元素,就尝试把它加入到序列中,如果当前的新元素比序列最后一个元素还要大,那么就直接加入,万事大吉,如果新元素没有最后一个元素大,我们就尝试在序列中找到第一个大于它的数(c++中的lower_bound函数),然后替换掉比它大的旧元素
为什么要这样做呢,因为如果我们能使一个序列中的元素尽可能地小,它就能加入更多的新元素对吧?

	3 2 9 6 7
1:	3
2:	2	替换,因为2更小
3:	2 9	加入
4:	2 6 替换,6更小
5:	2 6 7	加入

很简单对吧?🤣
重要的是上面分析过程中贪心的思想,有序我们也可以自然的想到使用二分搜索
奉上leetcode官网的一条评论
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(!nums.size())
            return 0;
        vector<int>maxlen;
        for(auto num:nums){
            if(maxlen.size()==0 or num>maxlen.back())
                maxlen.push_back(num);
            else if(num<maxlen.back()){
                // lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值 第一个 出现的位置
                // 即使不出现,lower_bound返回的是元素应该在数组中插入的位置
                int ind=lower_bound(maxlen.begin(),maxlen.end(),num)-maxlen.begin();
                maxlen[ind]=num;
            }    
        }
        return maxlen.size();
    }
};

在这里插入图片描述
偷懒用了一下lower_bound
贴一下lower_bound的实现,功能是返回第一个大于等于x的元素的位置
需要注意的是while条件和>=的条件
在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

hhmy77

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值