From e5ac146784afc5440508a5757caf17478d355ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Mon, 4 Mar 2024 15:33:30 +0800 Subject: [PATCH] 'commit' --- TangDou/AcWing_TiGao/T1/LIS/1010.md | 81 ++++++++++++++++++--- TangDou/AcWing_TiGao/T1/LIS/1010_TanXin.cpp | 35 +++++++-- 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/TangDou/AcWing_TiGao/T1/LIS/1010.md b/TangDou/AcWing_TiGao/T1/LIS/1010.md index 9d8e3d7..6ef24e9 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/1010.md +++ b/TangDou/AcWing_TiGao/T1/LIS/1010.md @@ -42,7 +42,79 @@ 因为拦截系统第一下是可以任意高的,以后只能拦截和它一样高,或者比它矮的,所以,这是一个**最长不上升子序列**。 **$Q2$:需要多少套拦截系统?** +这一问有两种办法,按思考的由易到难,说明贪心法与定理法: +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041334201.png) + +我们用两个贪心的证明方法来证明: +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041346994.png) + +![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041346474.png) + +> 解释: +> **$Q$:为什么一定可以放下去呢?** +> 答:因为$x$只能接在比它大的数字后面,由贪心法产生的序列,它前面是$a$,由最优解产生的序列,它前面是$b$,那么肯定有$a>=x,b>=x$,所以,是可以替换的。 + +#### $Code$ +```cpp {.line-numbers} +#include +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$ 长度 -#### 贪心法 -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041334201.png) - -我们用两个贪心的证明方法来证明: -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041346994.png) - -![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202403041346474.png) - - #### $Code$ 朴素作法 $O(N^2)$ ```cpp {.line-numbers} diff --git a/TangDou/AcWing_TiGao/T1/LIS/1010_TanXin.cpp b/TangDou/AcWing_TiGao/T1/LIS/1010_TanXin.cpp index 4ff8707..c79570b 100644 --- a/TangDou/AcWing_TiGao/T1/LIS/1010_TanXin.cpp +++ b/TangDou/AcWing_TiGao/T1/LIS/1010_TanXin.cpp @@ -5,11 +5,9 @@ const int N = 1010; int a[N], f[N]; // 每个导弹的高度,f[i]:以a[i]结尾的最长不升子序列长度 int q[N], ql; // 需要ql套拦截系统,每个拦截系统最后一个拦截高度为q[i] -int res; +int n, res; int main() { - // 用整行字符串+流的办法,读入多个带空格的数字 - int n; while (cin >> a[n]) n++; // 第一问 for (int i = 0; i < n; i++) { @@ -21,12 +19,33 @@ int main() { } // 第二问 for (int i = 0; i < n; i++) { - // 用贪心的思想来模拟创建新的导弹系统,或者,加入一个比h[i]大的导弹系统,并且,尾巴值最小的那套导弹系统中去 - // q[cnt] : 现在共cnt套导弹系统,q[i]记录的是每套导弹系统中最后一个拦截的高度 - // int k = 0; - // while (k < ql && q[k] < a[i]) k++; - int p = lower_bound(q, q + ql, a[i]) - q; + /* 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