diff --git a/TangDou/AcWing_TiGao/T1/LIS/272.md b/TangDou/AcWing_TiGao/T1/LIS/272.md
index bea07bf..903fe59 100644
--- a/TangDou/AcWing_TiGao/T1/LIS/272.md
+++ b/TangDou/AcWing_TiGao/T1/LIS/272.md
@@ -44,9 +44,10 @@ $1≤N≤3000$,序列中的数字均不超过 $2^{31}−1$。
### 二、前导知识
-[$AcWing$ $895$. 最长上升子序列](https://www.cnblogs.com/littlehb/p/15425546.html)
+**[$AcWing$ $895$. 最长上升子序列](https://www.cnblogs.com/littlehb/p/15425546.html)**
-**状态表示** $f[i]$表示从第一个数字开始算,以$a[i]$ **结尾** 的最长的上升序列长度。(以$a[i]$结尾的所有上升序列中属性为**最长**的那一个)
+**状态表示**
+$f[i]$表示从第一个数字开始算,以$a[i]$ **结尾** 的最长的上升序列长度。(以$a[i]$结尾的所有上升序列中属性为**最长**的那一个)
**状态计算**
@@ -57,7 +58,7 @@ f[i] = max(f[i], f[j] + 1) & 0 \le jb_k$$
+$$\large f[i][j]=max(f[i][j],f[i−1][k]+1),k∈[0,j−1],a_i=b_j,b_j>b_k$$
-如果直接按上述思路实现,需要三重循环:
+按上述思路实现,需要三重循环:
-```cpp {.line-numbers}
-for (int i = 1; i <= n; i ++ ){
- for (int j = 1; j <= n; j ++ ){
- f[i][j] = f[i - 1][j]; //不管a[i]是否等于b[j],f[i][j]一定会从f[i-1][j]继承过来
- if (a[i] == b[j]){
- int maxv = 1; //最起码命中了一个a[i]==b[j],LCIS最少是1
- for (int k = 1; k < j; k ++ )
- if (a[i] > b[k]) // 不光是公共,还要上升
- maxv = max(maxv, f[i - 1][k] + 1);
- f[i][j] = max(f[i][j], maxv);
- }
- }
-}
-```
-#### 实现代码$O(N^3)$
```cpp {.line-numbers}
#include
using namespace std;
@@ -122,7 +97,7 @@ const int N = 3010;
int a[N], b[N];
int f[N][N];
int res;
-
+// 通过了 10/13个数据
// O(n^3)
int main() {
int n;
@@ -138,14 +113,14 @@ int main() {
f[i][j] = f[i - 1][j];
// ③ 如果恰好 a[i]==b[j],那么就可以发生转移
if (a[i] == b[j]) {
- int maxv = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~
+ int mx = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~
// f[i-1]是肯定的了,问题是b的前驱在哪里?需要枚举1~j-1
for (int k = 1; k < j; k++)
- if (a[i] > b[k]) // 公共还不成,还需要上升
+ if (b[j] > b[k]) // 需要上升
// 找出公共且最长的
- maxv = max(maxv, f[i - 1][k] + 1);
+ mx = max(mx, f[i - 1][k] + 1);
// 更新答案
- f[i][j] = max(f[i][j], maxv);
+ f[i][j] = max(f[i][j], mx);
}
}
int res = 0;
@@ -158,9 +133,9 @@ int main() {
### 四、优化
-**$Q$:朴素办法超时($10/16$),如何优化?**
+**$Q$:朴素办法超时($10/13$),如何优化?**
-观察到,对于第二种状态转移:$f_{i,j}=max(f_{i,j},f_{i−1,k}+1) \ k∈[0,j−1],a_i=b_j,b_j>b_k$
+观察到,对于第二种状态转移:$f[i][j]=max(f[i][j],f[i−1][k]+1) \ k∈[0,j−1],a_i=b_j,b_j>b_k$
每次用到的 **状态** 都是第 $i - 1$ 个阶段的
@@ -186,11 +161,11 @@ int main() {
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
- int maxv = 1;
+ int mx = 1;
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j];
- if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv);
- if (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
+ if (a[i] == b[j]) f[i][j] = mx;
+ if (a[i] > b[j]) mx = max(mx, f[i - 1][j] + 1);
}
}
@@ -199,6 +174,7 @@ int main() {
return 0;
}
+
```
### 五、空间优化
diff --git a/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp b/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp
index 2695705..d08f917 100644
--- a/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp
+++ b/TangDou/AcWing_TiGao/T1/LIS/272_1.cpp
@@ -5,7 +5,7 @@ const int N = 3010;
int a[N], b[N];
int f[N][N];
int res;
-
+// 通过了 10/13个数据
// O(n^3)
int main() {
int n;
@@ -21,14 +21,14 @@ int main() {
f[i][j] = f[i - 1][j];
// ③ 如果恰好 a[i]==b[j],那么就可以发生转移
if (a[i] == b[j]) {
- int maxv = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~
+ int mx = 1; // 最起码a[i]==b[j],有一个数字是一样嘀~
// f[i-1]是肯定的了,问题是b的前驱在哪里?需要枚举1~j-1
for (int k = 1; k < j; k++)
- if (a[i] > b[k]) // 公共还不成,还需要上升
+ if (b[j] > b[k]) // 需要上升
// 找出公共且最长的
- maxv = max(maxv, f[i - 1][k] + 1);
+ mx = max(mx, f[i - 1][k] + 1);
// 更新答案
- f[i][j] = max(f[i][j], maxv);
+ f[i][j] = max(f[i][j], mx);
}
}
int res = 0;
diff --git a/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp b/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp
index 88309d5..4f318c4 100644
--- a/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp
+++ b/TangDou/AcWing_TiGao/T1/LIS/272_2.cpp
@@ -14,11 +14,11 @@ int main() {
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
- int maxv = 1;
+ int mx = 1;
for (int j = 1; j <= n; j++) {
f[i][j] = f[i - 1][j];
- if (a[i] == b[j]) f[i][j] = maxv;
- if (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
+ if (a[i] == b[j]) f[i][j] = mx;
+ if (a[i] > b[j]) mx = max(mx, f[i - 1][j] + 1);
}
}
diff --git a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md
index 507c62e..a5b94a8 100644
--- a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md
+++ b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md
@@ -327,7 +327,113 @@ $q[2]:7$
**[$AcWing$ $187$. 导弹防御系统](https://www.acwing.com/problem/content/189/)**
+导弹拦截系统的拦截高度即可以 严格单调上升,又可以严格单调下降,此时,用LIS模型是无法解决问题的,考虑到$n<50$,可以用$dfs+$剪枝
+
+```cpp {.line-numbers}
+#include
+
+using namespace std;
+const int N = 55;
+const int INF = 0x3f3f3f3f;
+int n;
+int a[N];
+int up[N], down[N];
+int res;
+
+// 本题关键字:贪心+爆搜
+
+/*
+ 功能:暴搜所有可能,配合剪枝,找出最少的拦截系统数量
+ u: 第几个导弹
+ ul: 上升拦截系统的数量,配合up 数组使用
+ dl: 下降拦截系统的数量,配合down数组使用
+ */
+void dfs(int u, int ul, int dl) {
+ if (ul + dl >= res) return; // 伟大的剪枝,不剪枝会TLE~,中途发现已经大于等于res的情况,就返回
+ if (u == n) { // 走完全程,收集答案
+ res = min(res, ul + dl); // 因为上面的剪枝,把ul+dl>=res的全干掉了,能到这里的,都是= a[u]) break;
+
+ t = down[k]; // 保留现场
+ down[k] = a[u];
+ if (k < dl)
+ dfs(u + 1, ul, dl);
+ else
+ dfs(u + 1, ul, dl + 1);
+ down[k] = t; // 回溯
+}
+
+int main() {
+ while (cin >> n, n) { // 多套数据,输入n=0时停止
+ for (int i = 0; i < n; i++) cin >> a[i];
+ res = INF; // 防御系统的最少数量
+ dfs(0, 0, 0); // 开始深搜,更新res的值
+ printf("%d\n", res);
+ }
+ return 0;
+}
+
+```
+
**[$AcWing$ $272$. 最长公共上升子序列](https://www.acwing.com/problem/content/274/)**
+
+```cpp {.line-numbers}
+#include
+
+using namespace std;
+const int N = 3010;
+int a[N], b[N];
+int f[N][N];
+int res;
+
+// O(n^2)
+int main() {
+ int n;
+ cin >> n;
+ for (int i = 1; i <= n; i++) cin >> a[i];
+ for (int i = 1; i <= n; i++) cin >> b[i];
+
+ for (int i = 1; i <= n; i++) {
+ int mx = 1;
+ for (int j = 1; j <= n; j++) {
+ f[i][j] = f[i - 1][j];
+ if (a[i] == b[j]) f[i][j] = mx;
+ if (a[i] > b[j]) mx = max(mx, f[i - 1][j] + 1);
+ }
+ }
+
+ for (int i = 1; i <= n; i++) res = max(res, f[n][i]);
+ printf("%d\n", res);
+
+ return 0;
+}
+
+```
[最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)