题解 - P1020
STA_Morlin · · 题解
$\text{UPD 2023.09.05}$:修改文中语义不明处。
P1020 导弹拦截 の 题目传送门。
题目简化
给定一个数列
b ,问:
- 它的最长不上升子序列长度;
- 最少能被划分成多少个不上升子序列。
思路讲解
- 一、为什么要求 最长不上升子序列:
但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。
rt,因为每一个系统的第
- 二、为什么要求 最长上升子序列:
Dilworth 定理
对偏序集
\langle A, \le \rangle ,设A 中最长链的长度是n ,则将A 中元素分成不相交的反链,反链个数至少是n 。
名词解释:
偏序关系:对于集合
偏序集:若在集合
反链:对于偏序集合
综上可简化为:最少的不上升子序列的个数就是最长上升子序列的长度。
偏序集
所以得出结论:导弹系统最少套数就是最长上升子序列的长度。
代码实现
一、求最长不上升子序列:
用普通的方法是不可以的,那个的时间复杂度是
所以需要用到 stl 函数:lower_bound 与 upper_bound。
对这个不熟悉的同学可以看 oiwiki STL 算法部分。
变量声明:
数组
数组
变量
把
-
如果
b_i \le l_{r1} ,说明b_i 可以直接加入l (而整个l 数组还是有序的); -
如果
b_i > l_{r1} ,说明若放入b_i 则l 会无序,所以要找办法把b_i 放进去:
怎么放呢?在
找到第一个小于 upper_bound 可以在
由于它返回的是指针就可以有一些奇特操作。
*upper_bound(l+1, l+r1+1, b[i], greater<int>()) = b[i];
证明:
更优性证明:
设找到的数为
-
如果
l_p 在末尾,由于l_p < b_i ,所以l_p 后面能接的没有b_i 多,l_p 让位给b_i 可以让序列更长。 -
如果
l_p 不在末尾,那l_p 以后都不会再被用到了,直接换了l_p 就行。
有序性证明:
设
因为
又因为
所以
综上,
实际上,
最后
二、求最长上升子序列长度
这里和上面很像,所以不用改比较器,而且因为是上升序列,所以要用 lower_bound。
基本上是“同上”二字就可以概括。
CODE
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int man = 1e5+10;
int n, r1, r2;
int a[man], l[man], h[man];
int main () {
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
while (scanf("%d", a+(++n)) != EOF) ; --n;
l[1] = h[1] = a[1]; r1 = r2 = 1;
for(int i = 2; i <= n; ++ i) {
if (l[r1] >= a[i]) l[++r1] = a[i];
else *upper_bound(l+1, l+r1+1, a[i], greater<int>()) = a[i];
if (h[r2] < a[i]) h[++r2] = a[i];
else *lower_bound(h+1, h+r2+1, a[i]) = a[i];
} printf("%d\n%d", r1, r2);
return 0;
}
扩展:Dilworth 定理的证明
施归纳于
-
当
n = 1 时:
命题显然成立。 -
假设对于
n = k 结论成立,考虑n = k+1 的情况:
当A 中最长链的长度为k+1 时,令M 为A 中极大元的集合,显然M 是一条反链。而且A-M 中最长链的长度为k 。
由归纳假设,可以把
因为