题目链接,(O(N^2))能过。
题目链接,(O(Nlog N))能过。
(O(N^2))做法
DP:f[i]表示,以第i个数结尾的最长子序列的集合的最大值。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N], 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; // 初始化,最小也有它一个数,所以初始化成1
for (int j = 1; j < i; j++)
if (a[j] < a[i])
f[i] = max(f[i], f[j] + 1);
}
int ans = 0;
for (int i = 1; i <= n; i++) ans = max(ans, f[i]);
cout << ans << endl;
return 0;
}
(O(Nlog N))做法
再开一个数组,记录长度为x的子序列中最后一个数最小的数。
例如 1 3 8 与 1 2 3 均为长度为三的序列,则新开的数组q[3] = 3
。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N], q[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int len = 0; // 用来记录找的过程中子序列的最大值
//int q[0] = -2e9;
for (int i = 1; i <= n; i++)
{
// 二分寻找q数组中小于a[i]的最大值
// 二分的套路:确定边界, 求mid, 设计check函数
int l = 0, r = len;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] < a[i]) l = mid;
else r = mid - 1;
}
len = max(len, r + 1); // 更新长度
q[r + 1] = a[i]; // 把a[i]放进去
}
cout << len << endl;
return 0;
}