|
|
@ -24,8 +24,18 @@ for (int i = 1; i <= n; i++) res = max(res, f[i]);
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $896$. 最长上升子序列 II ](https://www.acwing.com/problem/content/898/)**
|
|
|
|
**[$AcWing$ $896$. 最长上升子序列 II ](https://www.acwing.com/problem/content/898/)**
|
|
|
|
|
|
|
|
|
|
|
|
数据量增大:$N<=100000$
|
|
|
|
数据量增大:$N<=100000$,$O(LogN*N)$算法
|
|
|
|
$O(LogN*N)$算法
|
|
|
|
|
|
|
|
|
|
|
|
**状态表示**
|
|
|
|
|
|
|
|
$f[i]$表示长度为$i$的递增子序列中,末尾元素最小的是$f[i]$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**答案**
|
|
|
|
|
|
|
|
$fl$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**状态转移**
|
|
|
|
|
|
|
|
① $f[]$数组是一个单独上升的数组,这是可以二分的基础
|
|
|
|
|
|
|
|
② 每个长度都争取替换掉前面记录数组中第一个大于等于自己的数字,以保证长度不变的情况下,数字最小,因为只有最小才能让后面的其它数字更容易接上去,机会才能更多。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
// 1、首个元素入栈
|
|
|
|
// 1、首个元素入栈
|
|
|
@ -55,18 +65,6 @@ for (int i = 1; i <= n; i++) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
很明显,第一种写法性能更高,第二种写法有点暴力。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**状态表示**
|
|
|
|
|
|
|
|
$f[i]$表示长度为$i$的递增子序列中,末尾元素最小的是$f[i]$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**答案**
|
|
|
|
|
|
|
|
$fl$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**状态转移**
|
|
|
|
|
|
|
|
① $f[]$数组是一个单独上升的数组,这是可以二分的基础
|
|
|
|
|
|
|
|
② 每个长度都争取替换掉前面记录数组中第一个大于等于自己的数字,以保证长度不变的情况下,数字最小,因为只有最小才能让后面的其它数字更容易接上去,机会才能更多。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $897$. 最长公共子序列](https://www.acwing.com/problem/content/899/)**
|
|
|
|
**[$AcWing$ $897$. 最长公共子序列](https://www.acwing.com/problem/content/899/)**
|
|
|
|
|
|
|
|
|
|
|
|
**状态表示**
|
|
|
|
**状态表示**
|
|
|
@ -91,13 +89,196 @@ for (int i = 1; i <= n; i++)
|
|
|
|
$f[n][m]$
|
|
|
|
$f[n][m]$
|
|
|
|
|
|
|
|
|
|
|
|
#### 进阶题
|
|
|
|
#### 进阶题
|
|
|
|
AcWing 1017. 怪盗基德的滑翔翼
|
|
|
|
**[$AcWing$ $1017$. 怪盗基德的滑翔翼](https://www.acwing.com/problem/content/1019/)**
|
|
|
|
AcWing 1014. 登山
|
|
|
|
|
|
|
|
AcWing 482. 合唱队形
|
|
|
|
朴素$O(N^2)$版本
|
|
|
|
AcWing 1012. 友好城市
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
AcWing 1016. 最大上升子序列和
|
|
|
|
int mx = 0;
|
|
|
|
AcWing 1010. 拦截导弹
|
|
|
|
//从左到右,求一遍最长上升子序列
|
|
|
|
AcWing 187. 导弹防御系统
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
AcWing 272. 最长公共上升子序列
|
|
|
|
f[i] = 1;
|
|
|
|
|
|
|
|
for (int j = 1; j < i; j++)
|
|
|
|
|
|
|
|
if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
|
|
|
mx = max(mx, f[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//反向求解 LIS问题
|
|
|
|
|
|
|
|
for (int i = n; i >= 1; i--) {
|
|
|
|
|
|
|
|
f[i] = 1;
|
|
|
|
|
|
|
|
for (int j = n; j > i; j--)
|
|
|
|
|
|
|
|
if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
|
|
|
mx = max(mx, f[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
优化$O(LogN*N)$版本
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
// 正着求
|
|
|
|
|
|
|
|
f[0] = a[0];
|
|
|
|
|
|
|
|
for (int i = 1; i < n; i++) {
|
|
|
|
|
|
|
|
if (a[i] > f[fl])
|
|
|
|
|
|
|
|
f[++fl] = a[i];
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
*lower_bound(f, f + fl, a[i]) = a[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 反着求
|
|
|
|
|
|
|
|
g[0] = a[n-1];
|
|
|
|
|
|
|
|
for (int i = n - 1; i >= 0; i--) {
|
|
|
|
|
|
|
|
if (a[i] > g[gl])
|
|
|
|
|
|
|
|
g[++gl] = a[i];
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
*lower_bound(g, g + gl, a[i]) = a[i];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// pk的最大结果
|
|
|
|
|
|
|
|
res = max(fl, gl);
|
|
|
|
|
|
|
|
// 输出
|
|
|
|
|
|
|
|
printf("%d\n", res + 1);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1014$. 登山](https://www.acwing.com/problem/content/1016/)**
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
//正向求解 LIS问题
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
|
|
|
f[i] = 1;
|
|
|
|
|
|
|
|
for (int j = 1; j < i; j++)
|
|
|
|
|
|
|
|
if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//反向求解 LIS问题
|
|
|
|
|
|
|
|
for (int i = n; i >= 1; i--) {
|
|
|
|
|
|
|
|
g[i] = 1;
|
|
|
|
|
|
|
|
for (int j = n; j > i; j--)
|
|
|
|
|
|
|
|
if (a[i] > a[j]) g[i] = max(g[i], g[j] + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
//因为最终的那个中间点,左边计算了一次,右边双计算了一次,需要减1去重复
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) res = max(res, f[i] + g[i] - 1);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $482$. 合唱队形](https://www.acwing.com/problem/content/484/)**
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
//正向求解 LIS问题
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
|
|
|
f[i] = 1;
|
|
|
|
|
|
|
|
for (int j = 1; j < i; j++)
|
|
|
|
|
|
|
|
if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//反向求解 LIS问题
|
|
|
|
|
|
|
|
for (int i = n; i >= 1; i--) {
|
|
|
|
|
|
|
|
g[i] = 1;
|
|
|
|
|
|
|
|
for (int j = n; j > i; j--)
|
|
|
|
|
|
|
|
if (a[i] > a[j]) g[i] = max(g[i], g[j] + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
//因为最终的那个中间点,左边计算了一次,右边双计算了一次,需要减1去重复
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) res = max(res, f[i] + g[i] - 1);
|
|
|
|
|
|
|
|
//输出
|
|
|
|
|
|
|
|
printf("%d\n", n-res);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
//正向
|
|
|
|
|
|
|
|
f[++fl] = a[1];
|
|
|
|
|
|
|
|
p1[1] = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 2; i <= n; i++)
|
|
|
|
|
|
|
|
if (a[i] > f[fl]) {
|
|
|
|
|
|
|
|
f[++fl] = a[i];
|
|
|
|
|
|
|
|
p1[i] = fl;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
int t = lower_bound(f + 1, f + fl + 1, a[i]) - f;
|
|
|
|
|
|
|
|
f[t] = a[i];
|
|
|
|
|
|
|
|
p1[i] = t;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//反向
|
|
|
|
|
|
|
|
g[++gl] = a[n];
|
|
|
|
|
|
|
|
p2[n] = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = n - 1; i >= 1; i--)
|
|
|
|
|
|
|
|
if (a[i] > g[gl]) {
|
|
|
|
|
|
|
|
g[++gl] = a[i];
|
|
|
|
|
|
|
|
p2[i] = gl;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
int t = lower_bound(g + 1, g + gl + 1, a[i]) - g;
|
|
|
|
|
|
|
|
g[t] = a[i];
|
|
|
|
|
|
|
|
p2[i] = t;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) res = max(res, p2[i] + p1[i] - 1);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
**[$AcWing$ $1012$. 友好城市](https://www.acwing.com/problem/content/1014/)**
|
|
|
|
|
|
|
|
① 数对
|
|
|
|
|
|
|
|
② 左端点排序
|
|
|
|
|
|
|
|
③ 对于右端点组成的数组,求最长上升子序列长度
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++)cin >> q[i].first >> q[i].second;
|
|
|
|
|
|
|
|
sort(q + 1, q + 1 + n);//按first排序
|
|
|
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
|
|
|
f[i] = 1;
|
|
|
|
|
|
|
|
for (int j = 1; j < i; j++)
|
|
|
|
|
|
|
|
if (q[i].second > q[j].second)
|
|
|
|
|
|
|
|
f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
|
|
|
res = max(res, f[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%d\n", res);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
typedef pair<int, int> PII;
|
|
|
|
|
|
|
|
#define x first
|
|
|
|
|
|
|
|
#define y second
|
|
|
|
|
|
|
|
PII a[N]; // 南岸和北岸的一对友好城市的坐标
|
|
|
|
|
|
|
|
...
|
|
|
|
|
|
|
|
for (int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y;
|
|
|
|
|
|
|
|
sort(a + 1, a + 1 + n);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f[++fl] = a[1].y;
|
|
|
|
|
|
|
|
for (int i = 2; i <= n; i++) {
|
|
|
|
|
|
|
|
if (a[i].y > f[fl])
|
|
|
|
|
|
|
|
f[++fl] = a[i].y;
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
*lower_bound(f + 1, f + 1 + fl, a[i].y) = a[i].y;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
printf("%d\n", fl);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1016$. 最大上升子序列和](https://www.acwing.com/problem/content/1012/)**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 最大上升子序列和
|
|
|
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
const int N = 100010;
|
|
|
|
|
|
|
|
int a[N];
|
|
|
|
|
|
|
|
int f[N];//以第i个元素结尾的最大子序列和
|
|
|
|
|
|
|
|
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] = a[i]; // 最大上升子序列(个数)这里是1,此处是a[i]
|
|
|
|
|
|
|
|
for (int j = 1; j < i; j++)
|
|
|
|
|
|
|
|
// 最大上升子序列(个数)这里是加1,此处是+a[i]
|
|
|
|
|
|
|
|
if (a[i] > a[j]) f[i] = max(f[i], f[j] + a[i]);
|
|
|
|
|
|
|
|
res = max(res, f[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%d ", res);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
**魔改 最长上升子序列和**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $1010$. 拦截导弹](https://www.acwing.com/problem/content/1012/)**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $187$. 导弹防御系统](https://www.acwing.com/problem/content/189/)**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**[$AcWing$ $272$. 最长公共上升子序列](https://www.acwing.com/problem/content/274/)**
|
|
|
|
|
|
|
|
|
|
|
|
[最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)
|
|
|
|
[最长上升子序列 ($LIS$) 详解+例题模板 (全)](https://blog.csdn.net/lxt_Lucia/article/details/81206439)
|
|
|
|