|
|
|
@ -42,7 +42,79 @@
|
|
|
|
|
因为拦截系统第一下是可以任意高的,以后只能拦截和它一样高,或者比它矮的,所以,这是一个**最长不上升子序列**。
|
|
|
|
|
|
|
|
|
|
**$Q2$:需要多少套拦截系统?**
|
|
|
|
|
这一问有两种办法,按思考的由易到难,说明贪心法与定理法:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
我们用两个贪心的证明方法来证明:
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 解释:
|
|
|
|
|
> **$Q$:为什么一定可以放下去呢?**
|
|
|
|
|
> 答:因为$x$只能接在比它大的数字后面,由贪心法产生的序列,它前面是$a$,由最优解产生的序列,它前面是$b$,那么肯定有$a>=x,b>=x$,所以,是可以替换的。
|
|
|
|
|
|
|
|
|
|
#### $Code$
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|
#include <bits/stdc++.h>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
const int N = 1010;
|
|
|
|
|
|
|
|
|
|
int a[N], f[N]; // 每个导弹的高度,f[i]:以a[i]结尾的最长不升子序列长度
|
|
|
|
|
int q[N], ql; // 需要ql套拦截系统,每个拦截系统最后一个拦截高度为q[i]
|
|
|
|
|
int n, res;
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
while (cin >> a[n]) n++;
|
|
|
|
|
// 第一问
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
f[i] = 1;
|
|
|
|
|
for (int j = 0; j < i; j++)
|
|
|
|
|
if (a[i] <= a[j]) // 如果前面的比当前的大,说明是不升序列
|
|
|
|
|
f[i] = max(f[i], f[j] + 1);
|
|
|
|
|
res = max(res, f[i]);
|
|
|
|
|
}
|
|
|
|
|
// 第二问
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
/* 1、现有已经创建了ql套导弹系统
|
|
|
|
|
2、算法思路:
|
|
|
|
|
(1) 在已有的所有导弹拦截系统中找到大于当前高度,并且最小的那个,把当前导弹用这套拦截系统进行拦截
|
|
|
|
|
(2) 如果找不到这样的拦截系统,说明当前所有拦截系统的最小拦截高度都小于当前导弹,都无法利用,必须新开一个拦截系统。
|
|
|
|
|
(3) 也就是说,只有前面拦截系统的最后一个拦截高度,都小于当前高度时,才会创建新的,所以,后面的拦截系统,它的尾巴值应该是最大的
|
|
|
|
|
(4) 比如:
|
|
|
|
|
q[0]:5
|
|
|
|
|
q[1]:6
|
|
|
|
|
现在来了一个高度=4,根据上面的原则,应该放到q[0]里,修改q[0]=4
|
|
|
|
|
q[0]:4
|
|
|
|
|
q[1]:6
|
|
|
|
|
原来的数组是单调上升的,修改后也依然是单调上升的。
|
|
|
|
|
|
|
|
|
|
再比如:
|
|
|
|
|
q[0]:4
|
|
|
|
|
q[1]:6
|
|
|
|
|
q[2]:7
|
|
|
|
|
现在来了一个a[i]=5,我们肯定要修改q[1]=5,变为:
|
|
|
|
|
|
|
|
|
|
q[0]:4
|
|
|
|
|
q[1]:5
|
|
|
|
|
q[2]:7
|
|
|
|
|
原来的数组是单调上升的,修改后也依然是单调上升的。
|
|
|
|
|
|
|
|
|
|
既然有这个规律,那么就可以使用二分快速查找大于等于a[i]的位置p了:
|
|
|
|
|
*/
|
|
|
|
|
int p = lower_bound(q, q + ql, a[i]) - q;
|
|
|
|
|
if (p == ql) // 如果没有找到可以接在后面的导弹拦截系统,那么需要创建一套新的拦截系统
|
|
|
|
|
q[ql++] = a[i];
|
|
|
|
|
else
|
|
|
|
|
q[p] = a[i]; // 否则就直接接到找到的第k套拦截系统的后面,那么,第k套拦截系统的最后一个拦截高度=q[k]=h[i]
|
|
|
|
|
}
|
|
|
|
|
// 输出最长不升序列长度,即:最多能拦截的导弹数
|
|
|
|
|
printf("%d\n%d\n", res, ql);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### $Dilworth$ 定理
|
|
|
|
|
- **把序列分成不升子序列的最少个数,等于序列的最长上升子序列长度**
|
|
|
|
@ -54,15 +126,6 @@
|
|
|
|
|
|
|
|
|
|
问题一 套用 $LIS$ 模型即可求解。由 $Dilworth$ 定理,问题二等价于 另一个 $LIS$ 长度
|
|
|
|
|
|
|
|
|
|
#### 贪心法
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
我们用两个贪心的证明方法来证明:
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### $Code$
|
|
|
|
|
朴素作法 $O(N^2)$
|
|
|
|
|
```cpp {.line-numbers}
|
|
|
|
|