|
|
|
@ -1,10 +1,94 @@
|
|
|
|
|
## $LIS$+$LCS$专题
|
|
|
|
|
|
|
|
|
|
#### 基础题
|
|
|
|
|
[AcWing 895. 最长上升子序列](https://www.acwing.com/problem/content/897/)
|
|
|
|
|
[AcWing 896. 最长上升子序列 II ](https://www.acwing.com/problem/content/898/)
|
|
|
|
|
[AcWing 897. 最长公共子序列](https://www.acwing.com/problem/content/899/)
|
|
|
|
|
**[$AcWing$ $895$. 最长上升子序列](https://www.acwing.com/problem/content/897/)**
|
|
|
|
|
|
|
|
|
|
$O(N^2)$算法
|
|
|
|
|
|
|
|
|
|
**状态表示**
|
|
|
|
|
$f[i]$:以$a[i]$结尾的最长上升子序列长度
|
|
|
|
|
|
|
|
|
|
**状态转移**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
**答案**
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
for (int i = 1; i <= n; i++) res = max(res, f[i]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $896$. 最长上升子序列 II ](https://www.acwing.com/problem/content/898/)**
|
|
|
|
|
|
|
|
|
|
数据量增大:$N<=100000$
|
|
|
|
|
$O(LogN*N)$算法
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
// 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];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
也可以这样写
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
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[]$数组是一个单独上升的数组,这是可以二分的基础
|
|
|
|
|
② 每个长度都争取替换掉前面记录数组中第一个大于等于自己的数字,以保证长度不变的情况下,数字最小,因为只有最小才能让后面的其它数字更容易接上去,机会才能更多。
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $897$. 最长公共子序列](https://www.acwing.com/problem/content/899/)**
|
|
|
|
|
|
|
|
|
|
**状态表示**
|
|
|
|
|
定义$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]$
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
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. 怪盗基德的滑翔翼
|
|
|
|
@ -14,4 +98,6 @@ AcWing 1012. 友好城市
|
|
|
|
|
AcWing 1016. 最大上升子序列和
|
|
|
|
|
AcWing 1010. 拦截导弹
|
|
|
|
|
AcWing 187. 导弹防御系统
|
|
|
|
|
AcWing 272. 最长公共上升子序列
|
|
|
|
|
AcWing 272. 最长公共上升子序列
|
|
|
|
|
|
|
|
|
|
[最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)
|
|
|
|
|