📅  最后修改于: 2023-12-03 14:55:18.382000             🧑  作者: Mango
在算法竞赛和实际开发中,我们常常需要对一些序列进行一些操作和分析。其中,最长递增子序列被广泛地应用于许多场合。然而,在一些特殊的场合下,我们并不需要求出最长递增子序列本身,而只需要求出不属于最长递增子序列的所有元素的总和。
首先,我们需要了解一下什么是最长递增子序列(Longest Increasing Subsequence,简称 LIS)。
给定一个序列 $a_1,a_2,...,a_n$,我们称 $a_{i_1},a_{i_2},...,a_{i_k}$ 为 $a_1,a_2,...,a_n$ 的一个子序列,如果 $1\le i_1< i_2<...< i_k \le n$。该子序列是递增的,当且仅当 $a_{i_1}< a_{i_2}<...< a_{i_k}$。如果存在多个递增子序列的长度相同,那么我们称长度最大的递增子序列为最长递增子序列。
我们考虑一个问题:如何找到一个数列的最长递增子序列之外的所有数字的和最大。比如,通过考察下图,我们发现最长递增子序列为 1, 2, 5, 6,因此不属于最长递增子序列的元素是 4 和 3,其总和为 7。
1 2 5 6 3 4
对于这个问题,我们可以先求出最长递增子序列,然后将不在最长递增子序列中的元素求和,但是由于使用 LIS 算法的时间复杂度为 $O(n^2)$ 或者 $O(n\log n)$,并不适合求解这种问题。
事实上,我们可以不需要使用 LIS 的思想,也能够解决这个问题。我们考虑一个贪心的思路,不断地将未加入最长递增子序列(以下简称“未加入集合”)的最小元素加入集合中。因为加入未加入集合中的数字,最小数字会更小,从而使得后面的数字可能被加入到最长递增子序列中。因此,通过贪心策略,我们也能够求解最大化不属于 LIS 的所有元素的总和问题。
通过观察上面的贪心思路,我们可以给出下面的算法描述:
我们可以将上述想法翻译成 Python 代码。下面是对应的代码片段:
def solve(numbers):
ans = 0
minn = float('inf')
for i in numbers:
if i > minn:
minn = i
else:
ans += i
return ans
这个算法的时间复杂度为 $O(n)$,因此非常适合求解这个问题。
assert solve([1, 2, 5, 6, 3, 4]) == 7
assert solve([2, 1, 3, 5, 4]) == 4
assert solve([5, 4, 3, 2, 1]) == 10
上面的样例中,第一个样例已经在前面提到过了,第二个样例中,最长递增子序列为 1, 3, 5,因此不属于最长递增子序列的元素是 2 和 4,其总和为 4。第三个样例中不存在递增子序列,因此所有的数字都不属于递增子序列,其总和为 10。