最长上升子序列(贪心+二分)

Posted 4nc414g0n

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最长上升子序列(贪心+二分)相关的知识,希望对你有一定的参考价值。

最长上升子序列(二)

题目链接
题目描述:(贪心+二分,dp+二分
给定一个长度为 n 的数组a,求它的最长严格上升子序列的长度。
所谓子序列,指一个数组删掉一些数(也可以不删)之后,形成的新数组。例如 [1,5,3,7,3] 数组,其子序列有:[1,3,3]、[7] 等。但 [1,6]、[1,3,5] 则不是它的子序列。
我们定义一个序列是 严格上升 的,当且仅当该序列不存在两个下标 i 和 j 满足i<j 且 ai<=aj

输入:
[1,4,7,5,6]
输出:
4


思路

  1. 注意:dfs不能过
  2. 单dp也会超时
    当a[i]比a[j]大时,相当于长度比dp[j]时的状态加一,为dp[j]+1,状态转移方程: dp[i]=max(dp[j]+1,dp[i]);
for(int i=0;i<a.size();i++)

   for(int j=0;j<i;j++)
       if(a[i]>a[j])
           dp[i]=max(dp[j]+1,dp[i]);

  1. 使用贪心加二分查找
    贪心:我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小
    另开辟一个升序数组tail,用来表示这个最长的升序子序列,变量end记录tail的尾部index
    当index==0或者arr[index]>tail.back()的时候,直接将arr[index]加在tail最后面
    当arr[index]<tail.back(),使用二分查找在tail数组里找到第一个大于arr[index]的元素,将arr[index]赋给它
    返回end+1即为最长升序子序列个数

代码如下:

int BSearch(int left, int right, vector<int>& tail,int num)
   
       while(left<right)
       
           int mid=(left+right)/2;
           if(tail[mid]>num)
               right=mid;
           else
               left=mid+1; 
       
       return left;
   
   int LIS(vector<int>& a) 
       if(a.empty())
           return 0;
       vector<int> tail(a.size());
       int end=-1;
       for(int i=0;i<a.size();i++)
       
           if(i==0||tail[end]<a[i])
           
               end++;
               tail[end]=a[i];
           
           else
               tail[BSearch(0,end,tail,a[i])]=a[i];
       
       return end+1;
   
  1. 使用dp加二分查找
    更简洁的写法参考:300. 最长递增子序列(动态规划 + 二分查找,清晰图解)

以上是关于最长上升子序列(贪心+二分)的主要内容,如果未能解决你的问题,请参考以下文章

LIS 最长上升子序列问题(动态规划贪心+二分)

贪心+二分查找:最长上升子序列(3.14 leetcode每日打卡)

AcWing 895 最长上升子序列 - (线性DP) or (贪心+二分优化)

4/4 贪心选数+最长上升子序列+简单染色二分图+二进制搜索

1134 最长上升子序列 (序列型 DP)

精读《DOM diff 最长上升子序列》