最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 个不超过 1 0 6 10^6 106 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序 取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n n n,表示序列长度。
第二行有 n n n 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出 1 1 1、 2 2 2、 3 3 3、 4 4 4 即可。
思路
首先,定义一个整数 n
来存储序列的长度,和一个整数数组 a
来存储序列的元素。同时,定义一个长整型数组 dp
来存储动态规划的状态,dp[i]
存储的是长度为 i
的上升子序列的最小末尾元素。
在 main
函数中,首先读取序列的长度 n
,然后读取序列的元素。然后,对序列的每个元素进行处理,找到在 dp
数组中第一个大于或等于当前元素的位置 pos
。这里使用了 lower_bound
函数,这是一个标准库中的二分查找函数。如果 pos
大于当前的最长上升子序列的长度 len
,则更新 len
。最后,将当前元素放在 dp
数组的 pos
位置。
dp[i]
存储的是长度为 i
的上升子序列的最小末尾元素。当遍历到一个新的元素时,如果这个元素大于所有已有的上升子序列的末尾元素,则可以将这个元素接在最长的上升子序列后面,形成一个更长的上升子序列。否则,找到一个已有的上升子序列,它的末尾元素大于或等于这个元素,然后用这个元素替换那个末尾元素。这样做的目的是尽可能地使上升子序列的末尾元素小,以便有更多的机会接上新的元素。
最后,输出最长上升子序列的长度 len
。
AC代码
cpp
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;
const int N = 1e6 + 7;
int n;
int a[N];
ll dp[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int len = 0;
for (int i = 1; i <= n; i++) {
int pos = lower_bound(dp + 1, dp + len + 1, a[i]) - dp;
len = max(len, pos);
dp[pos] = a[i];
}
cout << len << "\n";
return 0;
}