main
黄海 1 year ago
parent d28142e20f
commit fc5fb21fa7

@ -1,29 +0,0 @@
#include <bits/stdc++.h>
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;
}

@ -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
```
<!-- 让表格居中显示的风格 -->
<style>
.center
{
width: auto;
display: table;
margin-left: auto;
margin-right: auto;
}
</style>
### 二、贪心+二分优化
#### 1、与朴素版本的区别
> **朴素版本状态表示**
> - 集合:$f[i]$表示从第一个数字开始算,以 $a[i]$<font color='red'><b> 结尾 </b></font>的最长的上升序列长度。
> - 属性:$max$
前一版本:$N≤1000$,本题要求:$N≤100000$
$N$的数据范围大了$100$倍,前一版本动态规划代码的时间复杂度是$O(N^2)$,$1000^2=1000000$,是$1e6$,是可以$1$秒过的,但如果是$100000^2=10000000000$,是$1e10$,超时,需要要优化~
#### 2、贪心+二分算法
><font color='red' size=4><b>核心思想:
对于同样长度的子串,希望它的末端越小越好,这样后面有更多机会拓展它,才有可能使得数列更长。</b></font>
> **状态表示**
> - 集合:$f[i]$表示长度为$i$的递增子序列中,末尾元素最小的是$f[i]$
> - 属性:$min$
**算法步骤**
* 扫描每个原序列中的数字:
* 如果$f$中的最后一个数字$f[idx]$小于当前数字$a[i]$,那么就在$f$的最后面增加$a[i]$
* 如果$a[i]$小于$f[idx]$,在$f$中查找并替换第一个大于等于它元素
**举栗子模拟**
<div class="center">
| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ |
| ----- | --- | --- | --- | --- | --- | --- | --- |
</div>
开始时$f[]$为空,数字$3$进入序列
<div class="center">
| $arr$ | <font color='red'>$3$</font> | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ |
| ----- | ---------------------------- | --- | --- | --- | --- | --- | --- |
| $f$ | <font color='red' size=3><b>$3$</b></font> |
| --- | ------------------------------------------ |
</div>
$1$ 比 $3$ 小, $3$出序列 $1$入序列
<div class="center">
| $arr$ | $3$ | <font color='red'> $1$ </font> | $2$ | $1$ | $8$ | $5$ | $6$ |
| ----- | --- | ------------------------------ | --- | --- | --- | --- | --- |
| $f$ | <font color='red' size=4><b>$1$</b></font> |
| --- | ------------------------------------------ |
</div>
$2$ 比 $1$ 大,$2$入序列
<div class="center">
| $arr$ | $3$ | $1$ | <font color='red'>$2$ </font> | $1$ | $8$ | $5$ | $6$ |
| ----- | --- | --- | ----------------------------- | --- | --- | --- | --- |
| $f$ | $1$ | <font color='red' size=4><b>$2$</b></font> |
| --- | --- | ------------------------------------------ |
</div>
$1$ 比 $2$ 小,在$f$中找到第一个大于等于$1$的位置,并替换掉原来的数字
<div class="center">
| $arr$ | $3$ | $1$ | $2$ | <font color='red'>$1$</font> | $8$ | $5$ | $6$ |
| ----- | --- | --- | --- | ---------------------------- | --- | --- | --- |
| $f$ | <font color='red' size=4><b>$1$</b></font> | $2$ |
| --- | ------------------------------------------ | --- |
</div>
$8$ 比 $2$ 大
<div class="center">
| $arr$ | $3$ | $1$ | $2$ | $1$ | <font color='red'>$8$</font> | $5$ | $6$ |
| ----- | --- | --- | --- | --- | ---------------------------- | --- | --- |
| $f$ | $1$ | $2$ | <font color='red' size=4><b>$8$</b></font> |
| --- | --- | --- | ------------------------------------------ |
</div>
$5$ 比 $8$ 小,在$f$中找到第一个大于等于$5$的数字,并替换掉原来的数字
<div class="center">
| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | <font color='red'>$5$</font> | $6$ |
| ----- | --- | --- | --- | --- | --- | ---------------------------- | --- |
| $f$ | $1$ | $2$ | <font color='red' size=4><b>$5$</b></font> |
| --- | --- | --- | ------------------------------------------ |
</div>
$6$ 比 $5$ 大
<div class="center">
| $arr$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | <font color='red' size=4><b>$6$ </b></font> |
| ----- | --- | --- | --- | --- | --- | --- | ------------------------------------------- |
| $f$ | $1$ | $2$ | $5$ | <font color='red' size=4><b>$6$</b></font> |
| --- | --- | --- | --- | ------------------------------------------ |
</div>
<font color='blue' size=6><b><center>$f$ 的长度$idx$就是最长递增子序列的长度</center></b></font>
#### 3、时间复杂度
$$\large O(N*logN)$$
### 四、实现代码
```cpp {.line-numbers}
#include <bits/stdc++.h>
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;
}
```

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

@ -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 <bits/stdc++.h>
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)$版本

Loading…
Cancel
Save