LIS最长上升子序列

一种方法是O(Nˆ2)的DP

主要是O(NlogN)的DP

最长上升子序列,最长不下降子序列,最长下降子序列,最长不上升子序列都同理

 

以LIS最长上升子序列为例

DP思路:

总的来说,我们要求以 i为结尾,最长的上升子序列的长度

现在到了第 i 个数,那么前面的 1~i-1的数都已经处理完毕了

设 f[x] 表示以 长度x的上升子序列中,子序列末端的最优值 ,此处f[x]就是保持最小值

比如 1,2,3,4 (5) 现在到了5,1~4都已经处理完毕,则长度为 3 的上升子序列的最优值是 3,不是4,f[3]=3

那么这有个巧妙的结论,f[x]一定是单调递增的,理解理解

1~i-1都已经处理完毕,对于 i 结尾的最长上升子序列,我们要找到最后一个小于 a[i] 的 f[x],那么对于 i 结尾的最长上升子序列的长度就是 x+1;

这过程可用二分处理,然后更新f[x+1],使得f[x+1]始终保持最优值。

    ans=0;
    memset(f,0,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        if(!ans) { ans=1,f[1]=a[1];continue; }
        int l=1,r=ans;
        while(l+1<r)
        {
            int mid=(l+r)/2;
            if(f[mid]<a[i]) l=mid;
            else r=mid; 
        }
        int len;
        if(f[r]<a[i]) len=r;
        else if(f[l]<a[i]) len=l;
        else len=0;
        if(!f[len+1]) f[len+1]=a[i];
        else f[len+1]=min(f[len+1],a[i]);
        if(len+1>ans) ans=len+1; 
    }
    printf("%d\n",ans);

 

 

那么以最长不上升子序列为例,f[x]是单调递减的,我们要找最后一个大于等于 a[i]  的 f[x]

f[x]要保持最优解,就是始终保持最大值。

    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(!ans) { ans=1,f[1]=a[1];continue; }
        int l=1,r=ans;
        while(l+1<r)
        {
            int mid=(l+r)/2;
            if(f[mid]>=a[i]) l=mid;
            else r=mid; 
        }
        int len;
        if(f[r]>=a[i]) len=r;
        else if(f[l]>=a[i]) len=l;
        else len=0;
        f[len+1]=max(f[len+1],a[i]);
        if(len+1>ans) ans=len+1; 
    }
    printf("%d\n",ans);

 

 

例题:

P1020 [NOIP1999 普及组] 导弹拦截 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目简化:

给定一个数列 b,问:

  1. 它的最长不上升子序列长度;
  2. 最少能被划分成多少个不上升子序列。

1.就不说了

2. Dilworth 定理 :最少的不上升子序列的个数就是最长上升子序列的长度 (记住就好)

posted @ 2023-01-23 21:26  QAQ啥也不会  阅读(18)  评论(0编辑  收藏  举报