📌  相关文章
📜  最小化数组中严格增加的子序列的数量|套装2(1)

📅  最后修改于: 2023-12-03 15:10:36.571000             🧑  作者: Mango

最小化数组中严格增加的子序列的数量 | 套装2

问题描述:

给定一个长度为 $n$ 的数组 $a$,请你计算最少需要划分几个子序列,使得每个子序列都是 $a$ 中的严格递增子序列。

解决方法:
思路

首先,我们需要明确的一点是,每个递增子序列中的元素不能跳着取,只能是连续的一段。

接下来,我们可以使用贪心的思想来解决该问题。假设当前访问到了 $a_i$,如果 $a_i$ 比之前所有访问到的元素都要大,那么 $a_i$ 肯定应该加入到之前访问到的某个子序列中。但如果 $a_i$ 比之前某个元素小,那么我们就需要新开一个子序列了。

算法

具体实现过程中,我们可以使用一个数组 $tail$,其下标 $i$ 表示当前使用长度为 $i$ 的递增子序列的最后一个元素的最小可能取值(也就是说,$tail_i$ 中存储了所有长度为 $i$ 的递增子序列的最后一个元素的最小取值)。比如,当 $tail$ 数组为 $[0, 0, 0, 0]$ 时,它表示目前使用长度为 $1$ 的递增子序列的最后一个元素的最小可能取值为 $0$。

接下来我们对数组 $a$ 进行遍历,对于每个 $a_i$,我们在 $tail$ 数组中找到一个最小的下标 $j$($1 \le j \le n$),使得 $tail_j < a_i$。此时 $a_i$ 肯定应该加入到长度为 $j$ 的递增子序列中,从而更新 $tail_j = a_i$。如果 $tail_j \ge a_i$,那么说明当前需要新开一个子序列,此时我们把 $a_i$ 加入到一个新的长度为 $j+1$ 的递增子序列中(因为此时已经没有长度为 $j$ 的递增子序列了),更新 $tail_{j+1} = a_i$。

经过上述操作后,$tail$ 数组中存储的就是所有递增子序列的最后一个元素中的最小值,它的长度就是所有子序列的个数。

代码
def min_increasing_subsequence_count(a: List[int]) -> int:
    n = len(a)
    tail = [0] * (n+1)
    len_of_subs = 0  # 当前递增子序列的长度

    for i in range(n):
        j = len_of_subs
        while j > 0 and tail[j] >= a[i]:
            j -= 1
        if j == len_of_subs:
            len_of_subs += 1
        tail[j+1] = a[i]

    return len_of_subs
复杂度分析

时间复杂度:遍历一次数组 $a$,每次操作可以看作是对长度为 $n$ 的递增序列的二分查找,所以时间复杂度是 $O(n \log n)$。

空间复杂度:额外使用了一个数组 $tail$,所以空间复杂度是 $O(n)$。

总结

本题相比于套装1所讲的问题增加了一些难度,但使用上述的思路,也可以用贪心的方式解决。