LIS优化(队列优化+二分查找)

  在介绍LIS之前先提供两个STL模板,lower_bound和upper_bound的模板,这两个分别是二分查找的上下界。

 1  int lower(int x,int y)
 2  {
 3      int mid;
 4      while(x<y){
 5          mid=x+(y-x)/2;//注意这里和(x+y)/2的区别!!! 
 6          if(a[mid]>=m)    y=m;
 7          else x=m+1;
 8      }
 9     return x; 
10  }
 1  int upper(int x,int y)
 2  {
 3      int mid;
 4      while(x<y){
 5          mid=x+(y-x)/2;
 6          if(a[mid]<=m)    x=m+1;
 7          else    y=m;
 8      }
 9     return x;
10  }

  接下来提供LIS的模板(求最大上升子序列O(n2)-> O(nlogn)),也可看洛谷的P1020的SPJ。

暴力枚举即每当插入一个新元素搜索前面的最大可能,dp方程为:

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

  那么我们提供如下的一种思路,假设我们现在有一个到i-1状态的最大连续上升子序列(此队列由i-1个数已求出):

  如果此时a[i]>a[i-1]那么直接插入就行,队列长度+1。

  但是如果这个数小于或等于a[i-1]的话呢?我们才用强插的方式(doge)。

  怎么插入呢?我们用logn的二分查找找出第一个大于等于a[i]的数,直接把他给replace掉,反正留着它也没什么用了,因为它没有后效性。

  这样的插入是完美的,可用反证法证明它的正确性。

 1 while(scanf("%d",&a[++n])!=EOF);    n--;
 2     s2[len2]=a[1];
 3     for(int i=2;i<=n;i++)
 4     {
 5         if(a[i]>s2[len2])    s2[++len2]=a[i];
 6         else{
 7             int p=lower_bound(s2+1,s2+len2,a[i])-s2;
 8             s2[p]=a[i];
 9         }
10     }

  那么下降呢,有的同学说这个lower_bound怎么找第一个小于它的数呢?

  自己手写呗!(doge)|| 手写个排序 || 参考priority_queue的原型。(priority_queue<int,vector<int>,greater<int> > que)


  最后介绍一个LIS优化的题目(uva1471)

  这一题的求解方式我们用了两次优化。

  第一次:我们先确定删除的子序列两端i和j,接下来往两边数.  Obviously:O(n3)

  第二次:我们先确定两个数组:g[i]表示以i结尾的上升子序列和f[i]表示的以i开始的上升子序列。初始化所用的时间O(n),总时间O(n2

  第三次:我们开LIS优化:用一个数组d[i]记录长度为 i 的连续递增序列的最后一个元素的最小值。用lower_bound够通过二分查找直接得到g[j]的值,进而得到一个可行的长度ans,然后更新数组d就可以。更新的方程如上面模板所求一致d[f[i]] = min(a[i], d[f[i]])。

  注:本题还有set的解法,代码量有点大,有兴趣可找我要。

 1   memset(dp,0x3f,sizeof(dp));
 2     scanf("%d",&n);
 3     for(int i=1;i<=n;i++)    scanf("%d",&a[i]);
 4     g[1]=1;    f[n]=1;
 5     for(int i=2;i<=n;i++)
 6         if(a[i]>a[i-1])    g[i]=g[i-1]+1;
 7         else    g[i]=1;
 8     for(int i=n-1;i>=1;i--)
 9         if(a[i]<a[i+1])    f[i]=f[i+1]+1;
10         else    f[i]=1;
11     for(int i=1;i<=n;i++)
12     {
13         int len=lower_bound(dp+1,dp+1+n,a[i])-(dp+1)+f[i];
14         ans=max(ans,len);
15         dp[g[i]]=min(dp[g[i]],a[i]);
16     }

  以上为基本的LIS优化方法,主旨在排除没有用的点减少不必要的查找,一般用于做子序列问题的dp题目。

posted @ 2021-08-09 23:15  Dydong  阅读(194)  评论(0编辑  收藏  举报