最长上升子序列
title: 最长上升子序列
categories:
- ICPC
tags:
- null
abbrlink: b5073ecc
date: 2024-04-12 00:00:00
最长上升子序列
我们讨论的是严格单调递增的LIS,然后考虑基本状态$dp[i]$表示以i结尾的LIS长度。状态转移是$dp_i=\max\limits_{j<i, a_j<a_i} dp_j+1$
朴素$O(n^2)$
void solve() {
int n;
cin >> n;
vector<int> dp(n + 1);
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
dp[i] = 1;
for (int j = 1; j <= i - 1; j++) {
if (a[j] < a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
}
int ans = *max_element(alls(dp));
cout << ans << endl;
}
用树状数组优化求dp前缀最大值,我们开一个权值树状数组,c[i]表示以值i结尾的LIS长度,每次只需要先查询后更新即可。
struct info {
int x = 0;
info operator+(const info &other) const {
return {max(x, other.x)};
}
};
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
int ans = 0;
Fwk<info> c(n);
for (int i = 1; i <= n; i++) {
int tmp = c.sum(a[i] - 1).x + 1;
c.add(a[i], {tmp});
deb(i, a[i], tmp);
ans = max(ans, tmp);
}
cout << ans << endl;
}
当数组有比较大的时候我们使用离散化即可
struct info {
int x = 0;
info operator+(const info &other) const {
return {max(x, other.x)};
}
};
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
//-----------
vector<int> b;
for (auto x : a) b.push_back(x);
sort(alls(b));
b.erase(unique(alls(b)), b.end());
auto find = [&](int x) {
return lower_bound(alls(b), x) - b.begin() + 1;
};
//-----------
int ans = 0;
Fwk<info> c(n);
for (int i = 1; i <= n; i++) {
int pos = find(a[i]);
int tmp = c.sum(pos - 1).x + 1;
c.add(pos, {tmp});
ans = max(ans, tmp);
}
cout << ans << endl;
}
贪心加二分解决这个问题:维护一个vector,dp[i]
表示长度为i的LIS的结尾元素的最小值。 然后考虑如何转移:每次二分更新即可
void solve() {
int n;
cin >> n;
vector<int> a(n + 1), dp(n + 1);
int len = 0;
for (int i = 1; i <= n; i++) cin >> a[i];
dp[0]=-inf;
for (int i = 1; i <= n; i++) {
if (a[i] > dp[len]) {
len++;
dp[len] = a[i];
} else {
int pos = lower_bound(dp.begin() + 1, dp.begin() + len + 1, a[i]) - dp.begin();
dp[pos] = a[i];
}
}
cout << len << endl;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!