From fc5fb21fa7beaa6c47f85553381ab009af166c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Wed, 6 Mar 2024 13:57:02 +0800 Subject: [PATCH] 'commit' --- TangDou/AcWing/LineDP/896.cpp | 29 --- TangDou/AcWing/LineDP/896.md | 210 ------------------ .../LineDP => AcWing_TiGao/T1/LIS}/895.cpp | 5 +- .../LineDP => AcWing_TiGao/T1/LIS}/895.md | 0 .../T1/LIS}/895_WithPrintPath.cpp | 0 TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md | 41 +++- 6 files changed, 35 insertions(+), 250 deletions(-) delete mode 100644 TangDou/AcWing/LineDP/896.cpp delete mode 100644 TangDou/AcWing/LineDP/896.md rename TangDou/{AcWing/LineDP => AcWing_TiGao/T1/LIS}/895.cpp (57%) rename TangDou/{AcWing/LineDP => AcWing_TiGao/T1/LIS}/895.md (100%) rename TangDou/{AcWing/LineDP => AcWing_TiGao/T1/LIS}/895_WithPrintPath.cpp (100%) diff --git a/TangDou/AcWing/LineDP/896.cpp b/TangDou/AcWing/LineDP/896.cpp deleted file mode 100644 index c892b3e..0000000 --- a/TangDou/AcWing/LineDP/896.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -using namespace std; -const int N = 100010; - -int n, a[N]; - -// 数组模拟栈 -int f[N], fl; - -int main() { - cin >> n; - for (int i = 0; i < n; i++) cin >> a[i]; - - // 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]; - } - // 栈内元素数量就是答案 - printf("%d\n", fl + 1); - return 0; -} \ No newline at end of file diff --git a/TangDou/AcWing/LineDP/896.md b/TangDou/AcWing/LineDP/896.md deleted file mode 100644 index 41be38b..0000000 --- a/TangDou/AcWing/LineDP/896.md +++ /dev/null @@ -1,210 +0,0 @@ -##[$AcWing$ $896$. 最长上升子序列 II](https://www.acwing.com/problem/content/description/898/) - -### 一、题目描述 - -给定一个长度为 $N$ 的数列,**求数值严格单调递增的子序列的长度最长** 是多少。 - -**输入格式** -第一行包含整数 $N$。 - -第二行包含 $N$ 个整数,表示完整序列。 - -**输出格式** -输出一个整数,表示最大长度。 - -**数据范围** -$1≤N≤100000$, -$−10^9≤数列中的数≤10^9$ - -**输入样例:** -```cpp {.line-numbers} -7 -3 1 2 1 8 5 6 -``` - -**输出样例**: -```cpp {.line-numbers} -4 -``` - - - - -### 二、贪心+二分优化 - -#### 1、与朴素版本的区别 - -> **朴素版本状态表示**: -> - 集合:$f[i]$表示从第一个数字开始算,以 $a[i]$ 结尾 的最长的上升序列长度。 -> - 属性:$max$ - - -前一版本:$N≤1000$,本题要求:$N≤100000$ - -$N$的数据范围大了$100$倍,前一版本动态规划代码的时间复杂度是$O(N^2)$,$1000^2=1000000$,是$1e6$,是可以$1$秒过的,但如果是$100000^2=10000000000$,是$1e10$,超时,需要要优化~ - - - -#### 2、贪心+二分算法 - ->核心思想: -对于同样长度的子串,希望它的末端越小越好,这样后面有更多机会拓展它,才有可能使得数列更长。 - -> **状态表示**: -> - 集合:$f[i]$表示长度为$i$的递增子序列中,末尾元素最小的是$f[i]$ -> - 属性:$min$ - -**算法步骤** -* 扫描每个原序列中的数字: - * 如果$f$中的最后一个数字$f[idx]$小于当前数字$a[i]$,那么就在$f$的最后面增加$a[i]$ - - * 如果$a[i]$小于$f[idx]$,在$f$中查找并替换第一个大于等于它元素 - -**举栗子模拟** -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | --- | --- | --- | --- | --- | - -
- - -开始时$f[]$为空,数字$3$进入序列 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | ---------------------------- | --- | --- | --- | --- | --- | --- | - -| $f$ | $3$ | -| --- | ------------------------------------------ | - -
- - -$1$ 比 $3$ 小, $3$出序列 ,$1$入序列 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | ------------------------------ | --- | --- | --- | --- | --- | - - -| $f$ | $1$ | -| --- | ------------------------------------------ | - -
- - -$2$ 比 $1$ 大,$2$入序列 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | ----------------------------- | --- | --- | --- | --- | - - -| $f$ | $1$ | $2$ | -| --- | --- | ------------------------------------------ | - -
- - -$1$ 比 $2$ 小,在$f$中找到第一个大于等于$1$的位置,并替换掉原来的数字 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | --- | ---------------------------- | --- | --- | --- | - - -| $f$ | $1$ | $2$ | -| --- | ------------------------------------------ | --- | -
- - -$8$ 比 $2$ 大 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | --- | --- | ---------------------------- | --- | --- | - - -| $f$ | $1$ | $2$ | $8$ | -| --- | --- | --- | ------------------------------------------ | -
- - -$5$ 比 $8$ 小,在$f$中找到第一个大于等于$5$的数字,并替换掉原来的数字 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | --- | --- | --- | ---------------------------- | --- | - - -| $f$ | $1$ | $2$ | $5$ | -| --- | --- | --- | ------------------------------------------ | -
- - -$6$ 比 $5$ 大 - -
- -| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ | -| ----- | --- | --- | --- | --- | --- | --- | ------------------------------------------- | - - -| $f$ | $1$ | $2$ | $5$ | $6$ | -| --- | --- | --- | --- | ------------------------------------------ | -
- - -
$f$ 的长度$idx$就是最长递增子序列的长度
- - -#### 3、时间复杂度 -$$\large O(N*logN)$$ - -### 四、实现代码 -```cpp {.line-numbers} -#include - -using namespace std; -const int N = 100010; - -int n, a[N]; - -// 数组模拟栈 -int f[N], fl; - -int main() { - cin >> n; - for (int i = 0; i < n; i++) cin >> a[i]; - - // 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]; - } - // 栈内元素数量就是答案 - printf("%d\n", fl + 1); - return 0; -} -``` \ No newline at end of file diff --git a/TangDou/AcWing/LineDP/895.cpp b/TangDou/AcWing_TiGao/T1/LIS/895.cpp similarity index 57% rename from TangDou/AcWing/LineDP/895.cpp rename to TangDou/AcWing_TiGao/T1/LIS/895.cpp index 63a4b87..79b9b0f 100644 --- a/TangDou/AcWing/LineDP/895.cpp +++ b/TangDou/AcWing_TiGao/T1/LIS/895.cpp @@ -12,9 +12,10 @@ int main() { for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) { - f[i] = 1; + f[i] = 1; // 一个以i结尾的序列,最少可以是i自己,长度是1 for (int j = 1; j < i; j++) - if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1); + if (a[i] > a[j]) // i可以接在j后面 + f[i] = max(f[i], f[j] + 1); // 借力于j res = max(res, f[i]); } printf("%d", res); diff --git a/TangDou/AcWing/LineDP/895.md b/TangDou/AcWing_TiGao/T1/LIS/895.md similarity index 100% rename from TangDou/AcWing/LineDP/895.md rename to TangDou/AcWing_TiGao/T1/LIS/895.md diff --git a/TangDou/AcWing/LineDP/895_WithPrintPath.cpp b/TangDou/AcWing_TiGao/T1/LIS/895_WithPrintPath.cpp similarity index 100% rename from TangDou/AcWing/LineDP/895_WithPrintPath.cpp rename to TangDou/AcWing_TiGao/T1/LIS/895_WithPrintPath.cpp diff --git a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md index a5b94a8..1d63b27 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md +++ b/TangDou/AcWing_TiGao/T1/LIS/LIS+LCS专题.md @@ -1,24 +1,47 @@ ## $LIS$+$LCS$专题 -#### 基础题 +#### 一、基础题 **[$AcWing$ $895$. 最长上升子序列](https://www.acwing.com/problem/content/897/)** -$O(N^2)$算法 +**时间复杂度** +$O(N^2)$ **状态表示** $f[i]$:以$a[i]$结尾的最长上升子序列长度 **状态转移** +$f[i] = max(f[i], f[j] + 1)(a[i] > a[j])$ + +**初始值** +$f[i] = 1$ +> **解释**:一个以$i$结尾的序列,最少可以是$i$自己,长度是$1$ + +**代码** ```cpp {.line-numbers} +#include + +using namespace std; +const int N = 1010; + +int n; +int a[N], f[N]; +int res; + +int main() { + cin >> n; + for (int i = 1; i <= n; i++) cin >> a[i]; + for (int i = 1; i <= n; i++) { - f[i] = 1; //只有a[i]一个数 + f[i] = 1; // 一个以i结尾的序列,最少可以是i自己,长度是1 for (int j = 1; j < i; j++) - if (a[j] < a[i]) f[i] = max(f[i], f[j] + 1); + if (a[i] > a[j]) // i可以接在j后面 + f[i] = max(f[i], f[j] + 1); // 借力于j + res = max(res, f[i]); } -``` -**答案** -```cpp {.line-numbers} -for (int i = 1; i <= n; i++) res = max(res, f[i]); + printf("%d", res); + return 0; +} + ``` @@ -88,7 +111,7 @@ for (int i = 1; i <= n; i++) **答案** $f[n][m]$ -#### 进阶题 +#### 二、进阶题 **[$AcWing$ $1017$. 怪盗基德的滑翔翼](https://www.acwing.com/problem/content/1019/)** 朴素$O(N^2)$版本