最长上升子序列 I(LIS)

题目链接
https://www.acwing.com/problem/content/897/

思路
闫氏dp分析法,先上图在这里插入图片描述
      状态表示图中已经说明,关于状态划分,f [ i ]表示的是所有以第i个数结尾的上升子序列的集合,那么这个集合的所有元素一定都是以第 i 个数结尾的,我们的划分与背包有点相似,以第(i-1)个数是哪个数来分类,那么可以分成:
      没有第(i-1)个数,序列长度为1,用0表示。
      第(i-1)个数是第1个数,用1表示。
      …
      第(i-1)个数是第 j 个数,用 j 表示。
      …
      第(i-1)个数是第(i-1)个数,用(i-1)表示。
      注意,上面说的第(i-1)个数,不是说原序列里面第 i 个数的前一个数,是f [ i ]所表示的每一个第 i 个数所表示的最长上升子序列的前一个数,然后这个数对应到原序列是哪一个数。
      当然,这里面每一类都不一定存在,如果a [ j ] ≥ a [ i ],那么这一类就不存在了,当然我们可以都放在这里,不存在的到时候判断一下不管就行,存在的取个max就行。
      那么存在的的怎么求?其实很简单,当你进行到第 i 个数的时候,前面(i-1)的状态肯定已经求过了,对于f [ i ]来说,就是 f [ j ] + 1(这个1就是加上a [ i ]本身),然后对 f [ i ] 和 (f [ j ] + 1)取个max即可,时间复杂度是O(n²),最后遍历 f 数组找到最大值即可,因为我们不知道以谁结尾会是最大值。
      这是LIS的朴素做法,它还有一个二分优化的版本,可以进一步降低时间复杂度。下面先贴朴素做法的代码。(二分优化我也还没彻底搞懂

代码

#include<bits/stdc++.h>
using namespace std;

int a[1005];
int f[1005];
int n;

int main()
{
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++)
    {
    
    
        f[i]=1;
        for(int j=1;j<i;j++)
        {
    
    
            if(a[i]>a[j])
            {
    
    
                f[i]=max(f[i],f[j]+1);
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++) res=max(res,f[i]);

    printf("%d\n",res);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Star_Platinum14/article/details/113101397