线性DP-最长上升子序列
最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 个不超过 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 ,表示序列长度。
第二行有 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
题目
动态规划:
-
状态表示:
f[i]
表示从第一个数字开始算,以w[i]
结尾的最大的上升序列 -
状态计算(集合的划分):
j∈(0,1,2,...,i-1)
,在w[i]>w[j]
时,f[i]=max(f[i],f[j]+1)
; -
- 有一个边界,若前面没有比
i
小的,那么f[i]=1
(以自己为结尾)
- 有一个边界,若前面没有比
-
状态转移方程:
f[i]=max(f[i],f[j]+1)
二分优化:
状态表示:f[i]
表示长度为i
的最长上升子序列末尾最小的数字
状态计算:对于每个w[i]
,
- 如果
w[i]>f[cnt]
,那么就吧这个元素加入到f[++cnt]=w[i]
- 否则找到第一个大于等于
w[i]
的元素,更新它为w[i]
代码
二维
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> f(n + 1, 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) f[i] = max(f[i], f[j] + 1);
}
}
int t = -1;
for (int i = 1; i <= n; i++) t = max(t, f[i]);
cout << t << endl;
return 0;
}
二分优化:
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main() {
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
int n;
cin >> n;
vector<int> a(n + 1);
vector<int> f;
for (int i = 1; i <= n; i++) cin >> a[i];
f.push_back(a[1]);
for (int i = 2; i <= n; i++) {
if (a[i] >= f.back()) f.push_back(a[i]);
else *std::lower_bound(f.begin(), f.end(), a[i]) = a[i];
}
cout<<f.size();
return 0;
}
最长公共子序列
问题
https://www.acwing.com/problem/content/description/899/
给定两个长度分别为 N和 M 的字符串 A和 B,求既是 A的子序列又是 B的子序列的字符串长度最长是多少。
题目
-
dp[i][j]
表示所有由第一个序列的前i
个字母和第二个序列的前j
个字母构成的公共子序列的最大值 -
集合的划分
-
- 00
a[i],a[j]
都不选 :dp[i-1][j-1]
- 01 选
a[j]
,不选a[i]
:dp[i-1][j]
- 10 选
a[i]
,不选a[j]
:dp[i][j-1]
- 11 选
a[i],a[j]
:dp[i-1][j-1]+1
- 00