线性dp:最长上升子序列

最长上升子序列

  • 本文与leetcode300.最长递增子序列,这题题意一样,阅读完本文可以挑战一下

力扣链接

题目叙述:

给定一个无序的整数数组,找出其中最长上升子序列(LIS)的长度。

输入:

[5,7,1,9,4,6,2,8,3]

输出

4

解释

  • 最长上升子序列是[1,4,6,8],其长度为4。

动态规划的设计:

  • 首先,我们对数组进行下标的映射,我们从下标为1的位置开始计数

  • 并且,我们计算以每一个元素a【i】为结尾的最长上升子序列的长度f【i】。

img

  • 由上面这张图我们可知,最长的上升子序列可以由前面的状态推出,因此我们可以考虑使用动态规划算法

状态变量dp的含义:

  • 我们在这里数组名用f,而不用dp
  • f[i]代表以a[i]为结尾的最长上升子序列长度
  • 初始条件为f[i]=1

递推公式:

  • 我们需要一个j指针,每次遍历到a【i】时,我们都需要j指针,从1开始移动,移动到i-1的位置,如果发现a[i]>a[j],那么我们就更新f【i】的值,不过还得判断一下f【j】+1与f【i】的大小关系。
for(int i=2;i<=n;i++){
    for(int j=1;j<i;j++){
        if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
    }
    //ans为最终的最大上升子序列的长度
    ans=max(ans,f[i]);
}

遍历顺序:

  • 因为我们的状态变量f[i]是由f[j]决定的,所以说我们的遍历顺序显然是从前向后遍历

如何初始化?

  • 在上面已经说了:f[i]=1,因为每个数字至少都有以本身为序列的最长上升子序列。

举例打印dp数组

  • 下标 :1,2,3,4,5,6,7,8,9

  • a【i】:5,7,1,9,4,6,2,8,3

  • f【i】 :1,2,1,3,2,3,2,4,3

递推式满足的条件

  • 由小推大(最优子结构)
  • 由过去推现在(无后效性)

疑问:

  • f【i】记录以a【i】为开头的最长上升子序列可以吗?——可以,不过遍历顺序就是从后向前遍历,递推式也需要改变改变,这里读者可以自行推理!
  • f【i】记录前i个数的最长上升子序列的长度,可以吗?——不可以,举例两个就可以发现明显错误的反例,因此不成立。

代码实现:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N=1010;
int n, a[N];
int f[N];

int main(){
  cin>>n;
  for(int i=1; i<=n; i++) cin>>a[i];
  
  for(int i=1; i<=n; i++) f[i]=1;
  for(int i=1; i<=n; i++)
    for(int j=1; j<i; j++)
      if(a[j]<a[i]) f[i]=max(f[i],f[j]+1);
  
  int res=0;
  for(int i=1; i<=n; i++) res=max(res,f[i]);
  cout<<res;
}
posted @ 2024-08-23 01:05  Tomorrowland_D  阅读(42)  评论(0编辑  收藏  举报