main
黄海 1 year ago
parent ae6bce7770
commit 16c695e67c

@ -5,7 +5,7 @@ using namespace std;
int n; int n;
const int N = 100010; const int N = 100010;
int a[N]; int a[N];
int f[N]; int f[N];//以第i个元素结尾的最大子序列和
int res; int res;
int main() { int main() {

@ -54,7 +54,6 @@ $f[i]$:以第$i$个元素结尾的最大子序列和
### 三、$O(n^2)$实现代码 ### 三、$O(n^2)$实现代码
```cpp {.line-numbers} ```cpp {.line-numbers}
//运行时间: 31 ms
#include <bits/stdc++.h> #include <bits/stdc++.h>
using namespace std; using namespace std;
@ -62,7 +61,7 @@ using namespace std;
int n; int n;
const int N = 100010; const int N = 100010;
int a[N]; int a[N];
int f[N]; int f[N];//以第i个元素结尾的最大子序列和
int res; int res;
int main() { int main() {

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

Loading…
Cancel
Save