main
黄海 1 year ago
parent 133bb2bb88
commit ae6bce7770

@ -11,8 +11,10 @@ int main() {
cin >> n >> m >> a + 1 >> b + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
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]);
}
printf("%d", f[n][m]);
return 0;

@ -39,7 +39,6 @@ abedc
* 当$a[i] \neq b[j]$时:
* 如果$a[i]$不产生贡献,那么把它干掉$f[i-1][j]$
* 如果$b[j]$不产生贡献,那么把它干掉$f[i][j-1]$
* 如果两个都没有贡献,那么就是$f[i-1][j-1]$
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041643972.png)
@ -59,11 +58,12 @@ int main() {
cin >> n >> m >> a + 1 >> b + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
if (a[i] == b[j]) f[i][j] = f[i - 1][j - 1] + 1;
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]);
}
printf("%d", f[n][m]);
return 0;
}
```

@ -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)

@ -1,2 +0,0 @@
### 五、相关资源
[最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)
Loading…
Cancel
Save