题目地址:
https://www.acwing.com/problem/content/898/
给定一个长 N N N数列 a a a,求严格单调递增的子序列的最长长度。
数据范围:
1 ≤ N ≤ 100000 1\le N\le 100000 1≤N≤100000
− 1 0 9 ≤ x ≤ 1 0 9 -10^9\le x\le 10^9 −109≤x≤109
x x x是数列中的数
思路是偏序集分解定理。一个偏序集的最长链的长度,就是它能分解为最少多少个反链的个数。在这里可以将每个数与它的下标做成一个pair,并定义 ( i , a [ i ] ) < ( j , a [ j ] ) (i,a[i])<(j,a[j]) (i,a[i])<(j,a[j])当且仅当 i < j ∧ a [ i ] < a [ j ] i<j\land a[i]<a[j] i<j∧a[i]<a[j]。那么反链就是指 i < j ∧ a [ i ] ≥ a [ j ] i<j\land a[i]\ge a[j] i<j∧a[i]≥a[j]的情况了。我们只需要看最少能分解出多少个反链即可。思路参考https://blog.csdn.net/qq_46105170/article/details/108616895。这里用一个数组 f f f来存每条反链的最后一个数字。代码如下:
#include <iostream>
using namespace std;
const int N = 100010;
int n;
int a[N], f[N];
// 在f[0 : r]中找到第一个大于等于t的数字的下标
int binary_search(int f[], int r, int t) {
if (r < 0) return -1;
int l = 0;
while (l < r) {
int m = l + (r - l >> 1);
if (f[m] >= t) r = m;
else l = m + 1;
}
return f[l] >= t ? l : -1;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
int idx = 0;
for (int i = 0; i < n; i++) {
int pos = binary_search(f, idx - 1, a[i]);
if (pos == -1) f[idx++] = a[i];
else f[pos] = a[i];
}
cout << idx << endl;
return 0;
}
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。