最长上升子序列——动态规划(C++详解)

题目描述

LIS(Longest Increasing Subsequence)最长上升子序列
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。
比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).
对于给定的序列,求出最长上升子序列的长度。

输入样例

5
4 2 3 1 5

输出样例:

3

题解

最长上升子序列是动态规划中一个非常典型的例题,他将动态规划的思想完美的融合进去(将一个问题划分成若干个最优的子问题,并且子问题的推导均与上一个子问题有关1

这一题将在下面进行递推式的推导

思路

求解这个问题,我们可以把它分解出来——下一个子序列长度len[i+1]是上一个最长子序列的长度加1(a[i+1]>a[j])(j为前面的子序列的最后一项)或者1(当前面的所有子序列的最后一项都比a[i+1]大,此时子序列就为他本身,len[i+1]只能为1)
这是是什么意思呢
就是将这个子序列长度的状态变化全部用一个len数组存储下来
举个例子
{3,1,4,1,5,9,2,6,5}这个序列的最长上升子序列

i=1 a[1]=3 len[1]=1 序列为 3

i=2 a[2]=1 len[2]=1 序列为1
(因为a[2]与a[1]比较,a[2]<a[1],所以含有a[2]的子序列不可能是将a[2]加到a[1]后方,此时只能是a[2]单独为一个子序列

i=3,a[3]=4 len[3]=2 序列为3 4或1 4
(因为a[3]与前面的a[1]和a[2]比较,a[3]都大于a[1]和a[2],所以a[3]可以加到a[1]或者a[2]的后方,此时序列便为a[1],a[3]或a[2],a[3]

i=4 a[4]=1 len[1]=1 序列为1(单独成列)

i=5 a[5]=5 len[5]=3 序列为3 4 5或1 4 5(加到i=3后方)

i=6 a[6]=9 len[6]=4 序列为3 4 5 9或1 4 5 9(加到i=5后方)

i=7 a[7]=2 len[7]=2 序列为1 2或1 2(加到i=2或i=4后方)

i=8 a[8]=6 len[8]=4 序列为3 4 5 6或1 4 5 6(加到i=5后方)

i=9 a[9]=3 len[9]=3 序列为3 4 5或1 2 5(加到i=3后方)

从上面过程可以看出,最长上升子序列为4(len[6]或len[8])
按照这个思路,便可以求解这道题

递推关系式为:dp[i+1]=max{1(a[i+1]<a[j])(j为0到i),dp[j]+1(a[i]>a[j])}

代码如下

#include <windows.h>
#include <iostream>

int len[2000];//dp数组
int a[2000];
int main() {
  int n;
  int sum = 0;
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  for (int i = 1; i <= n; i++) {
  //将此时的最大子序列暂定为自身
    len[i] = 1;
  //如果此时a[i]大于a[j]的话,此时最大上升子序列长度为前面最大子序列长度加1
    for (int j = 1; j < i; j++) {
      if (a[i] > a[j]&&len[i]<len[j]+1){
        len[i] = len[j] + 1;
      }
    }
  //sum为前面所有已经求出最长上升子序列的最大长度
    sum = max(sum, len[i]);
  }
  printf("%d", sum);
  return 0;
}

  1. 动态规划的具体思想本文省略 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值