Files
python/TangDou/AcWing/LIS/896.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

212 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

##[$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]$
> - 属性:这个挺特殊,不是$max,min$,而是最终数列的长度$fl$
**算法步骤**
> $f[fl]$:$f$中的最后一个数字,$fl$是数组中的游标。
扫描每个原序列中的数字$a[i]$:
① 如果$a[i]$大于$f[fl]$,$f[++fl]=a[i]$,把$a[i]$ **接** 在$f[]$数组的最后面
② 如果$a[i]$小于$f[fl]$,在$f$中查找并替换第一个大于等于它元素
> **解释**:这样操作就可以使得长度一样的上升子序列的每个位置是最小的数字!
**举栗子**
<div class="center">
| $a$ | $3$ | $1$ | $2$ | $1$ | $8$ | $5$ | $6$ |
| --- | --- | --- | --- | --- | --- | --- | --- |
</div>
开始时$f[]$为空,数字$3$进入序列
<div class="center">
| $a$ | <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">
| $a$ | $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">
| $a$ | $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">
| $a$ | $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">
| $a$ | $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">
| $a$ | $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">
| $a$ | $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$ 的长度$fl$就是最长递增子序列的长度</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;
}
```