You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3.3 KiB
3.3 KiB
LIS
+LCS
专题
基础题
O(N^2)
算法
状态表示
f[i]
:以a[i]
结尾的最长上升子序列长度
状态转移
for (int i = 1; i <= n; i++) {
f[i] = 1; //只有a[i]一个数
for (int j = 1; j < i; j++)
if (a[j] < a[i]) f[i] = max(f[i], f[j] + 1);
}
答案
for (int i = 1; i <= n; i++) res = max(res, f[i]);
数据量增大:N<=100000
O(LogN*N)
算法
// 1、首个元素入栈
f[0] = a[0];
// 2、后续元素开始计算
for (int i = 1; i < n; i++) {
if (a[i] > f[fl])
f[++fl] = a[i];
else
//利用STL的二分,在f中查找第一个大于等于a[i]的值,并完成替换
*lower_bound(f, f + fl, a[i]) = a[i];
}
也可以这样写
memset(f,-0x3f,sizeof f);
// 2、后续元素开始计算
for (int i = 1; i <= n; i++) {
if (a[i] > f[fl])
f[++fl] = a[i];
else
//利用STL的二分,在f中查找第一个大于等于a[i]的值,并完成替换
*lower_bound(f + 1, f + 1 + fl, a[i]) = a[i];
}
很明显,第一种写法性能更高,第二种写法有点暴力。
状态表示
f[i]
表示长度为i
的递增子序列中,末尾元素最小的是f[i]
答案
fl
状态转移
① f[]
数组是一个单独上升的数组,这是可以二分的基础
② 每个长度都争取替换掉前面记录数组中第一个大于等于自己的数字,以保证长度不变的情况下,数字最小,因为只有最小才能让后面的其它数字更容易接上去,机会才能更多。
状态表示
定义f[i][j]
是a[]
以i
结尾,b[]
以j
结尾的最长公共子序列长度
说明:没有说
a[i]
或者b[j]
一定要出现在最长公共子序列当中!这个最长公共子序列,可能是a[]
和b[]
的一些前序组成的,a[i],b[j]
也可能没有对结果产生贡献。
-
当
a[i]==b[j]
时,看一下两个字符串的前序,发现在少了a[i],b[j]
后,转化为子问题f[i-1][j-1]
,问题转化为$f[i][j]=f[i-1][j-1]+1
$ -
当
a[i] \neq b[j]
时:- 如果
a[i]
不产生贡献,那么把它干掉f[i-1][j]
- 如果
b[j]
不产生贡献,那么把它干掉f[i][j-1]
- 如果
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (a[i] == b[j])
f[i][j] = f[i - 1][j - 1] + 1;
else
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
答案
f[n][m]
进阶题
AcWing 1017. 怪盗基德的滑翔翼 AcWing 1014. 登山 AcWing 482. 合唱队形 AcWing 1012. 友好城市 AcWing 1016. 最大上升子序列和 AcWing 1010. 拦截导弹 AcWing 187. 导弹防御系统 AcWing 272. 最长公共上升子序列