最长上升子序列问题
CYY

题目来源

https://www.acwing.com/problem/content/897/


题目

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数 N。

第二行包含 N 个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000,

$−10^9$≤数列中的数≤$10^9$

输入样例

1
2
7
3 1 2 1 8 5 6

输出样例

1
4

dp问题解决思路

状态表示dp[i]

某长度为i的序列的最后一位元素,比如1,2,6序列则可用状态dp[3]来表示 = 6

状态dp[i]转移到dp[i+1]

对于小于最长子序列长度的所有子序列状态,有两种可能

  1. i为最长子序列长度时,这时候dp[i]转移到dp[i+1]即是在dp[i+1]所表示序列的基础上加上一个元素,最长序列长度加1。

  2. i不为最长子序列长度时,这时候dp[i]转移到dp[i+1]即是更新dp[i+1]所表示的序列考虑一个新元素num

    • 对于情况(1),只要大与最长上升子序列即可完成状态转移,dp[i+1] = num,比如dp[3]表示的序列为1,2,4,且最长子序列长度就为3,对于新元素5那么dp[4] = 5,所代表的序列就是1,2,4,5,最长序列长度加1
    • 对于情况(2),如果新元素num比待更新的dp[i+1]小,则由dp[i]加上新元素对dp[i+1]进行更新如果大则不更新比较好,我们以较小数结尾肯定比以较大数结尾对后续子串的作用更大。比如原dp[3]表示的子序列是1,2,4,dp[4]表示的子序列是1,2,4,8现在来了新元素5对于dp[3]转移到dp[4],就是将1,2,4加上新元素5更新dp[4],即dp[4]从原来的8变成了5.反之dp[4]等于5肯定比dp[4] = 8好啊,因为,从原来要比8大的数才能增加最长序列长度,变成只需要大于5就行!
状态初始化

情况1满足的条件是num>dp[i],情况2满足的条件是num>dp[i]且num<dp[i+1]。所以如果我们将dp数组默认值都设置为最大值,两种情况就可以合并为情况2来考虑。只需要特殊判断一下满足条件时i是否等于最大上升序列长度,如果是则更新最长上升序列长度

AC代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <climits>
#include <cstring>
using namespace std;

int main(){
int n,len = 0,dp[1004];
cin>>n;
//默认状态数组为最大值,都两种状态变一种状态考虑
for(int i = 1; i < 1004; i++) dp[i] = INT_MAX;

dp[0] = INT_MIN; //我们默认最长子序列长度为0,dp[0]没意义设为最小值
for(int i = 0;i<n;i++){
int num;
cin>>num;
//每次读入一个新元素
for(int j = 0;j<=len;j++){
//从0开始遍历到最长子串长度
if(num>dp[j]&&num<dp[j+1]){
//统一用情况2考虑
dp[j+1] = num;//状态更新
if(j == len) len++; //特殊判断一下情况1,如果情况1就更新最长序列长度
}
}
}
cout<<len;
return 0;
}
 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep