零、回顾最长上升子序列的 O ( n log n ) O(n\log n) O(nlogn) 做法
设当前最长上升子序列的长度为 l e n len len,额外数组 q [ i ] q[i] q[i] 记录当前所有长度为 i i i 的上升子序列的末尾元素的最小值。
从前往后对于每一个元素 a [ i ] a[i] a[i]:
- a [ i ] > q [ l e n ] a[i] > q[len] a[i]>q[len],那么 a [ i ] a[i] a[i] 可以接在当前最长上升子序列的后面,更新当前最长上升子序列长度 ( l e n = l e n + 1 len=len+1 len=len+1),更新长度为 l e n + 1 len+1 len+1 的上升子序列的末尾元素的最小值 ( q [ l e n + 1 ] = a [ i ] q[len+1]=a[i] q[len+1]=a[i]);
- 否则,二分 (可以证明 q q q 数组中的元素一定单调增,这是降低时间复杂度的关键) 找到 q q q 数组中第一个大于等于 a [ i ] a[i] a[i] 的元素,用 a [ i ] a[i] a[i] 更新它。
一、拦截导弹
第一问:求最长非上升子序列长度;
第二问:求最少用几个非上升子序列可以覆盖整个序列。
第二问分析:对于每一个导弹,有以下两种选择:
- 用现有的某套系统去拦截它,即把该元素接在某个非上升子序列的末尾;
- 新创建一个系统,即该元素作为新的非上升子序列的开头。
贪心流程:从前往后对于每一个导弹:
- 若现有的所有非上升子序列的末尾都小于这个数,则创建一个新的子序列;
- 否则找到末尾大于等于这个数且最小的非上升子序列,把这个数接到这个子序列的后面。
贪心证明:
- 证明两个数相等:设 A A A 表示贪心算法得到的序列个数, B B B 表示最优解,一定有 B ≤ A B\le A B≤A,因此只要证明 A ≤ B A\le B A≤B 即可。
- 调整法:假设最优解对应方案与当前方案不同,找到第一个不同的数。
设当前的导弹为 x x x,当前所有的非上升子序列:
_ _ _ _ _ _ _
_ _ _ _ _ _ a a a
_ _ _ _ _ _ _
_ _ _ _ _ _ b b b
其中贪心法方案是将 x x x 接在 a a a 的后面,最优解方案是将 x x x 接在 b b b 的后面。
由贪心法流程可知 a ≤ b a\le b