假设我们已经得到了给定长度n
的整数序列。我们想要删除一些元素(可能没有),以便序列在结果中轮流增加和减少。这意味着,每个元素都应该具有相邻元素,既可以更大,也可以比自身小。
例如,1 3 2 7 6
和5 1 4 2 10
都是顺序递增和递减的序列。
我们想要删除一些元素来转换我们的序列,但我们也希望最大化剩余元素的总和。因此,例如,从序列2 18 6 7 8 2 10
开始,我们要删除6
并将其设为2 18 7 8 2 10
。
我正在寻找解决这个问题的有效方法。上面的例子表明,最天真的贪心算法(删除每个破坏序列的第一个元素)都不会起作用 - 它会删除7
而不是6
,这不会最大化元素之和剩下。
任何想法如何有效地解决(可能O(n) or O(n log n)
)正确?
答案 0 :(得分:0)
对于索引为i
的序列的每个元素,我们将计算F(i, high)
和F(i, low)
,其中F(i, high)
等于子序列的最大总和,其中所需特征结束使用i
- 元素,此元素是“高峰”。 (我将主要解释“高”部分,“低”部分可以类似地完成)。我们可以使用以下关系计算这些函数:
答案在所有F(i, high)
和F(i, low)
值中最大。
这为我们提供了一个相当简单的动态编程解决方案,其时间复杂度为O(n^2)
。但我们可以走得更远。
我们可以优化max(F(j,low))
部分的计算。我们需要做的是找到先前计算的F(j, low)
中具有a[j] < a[i]
条件的最大值。这可以使用segment trees完成。
首先,我们将“挤压”我们的初始序列。只有在计算总和时,我们才需要元素a[i]
的实际值。但在检查a[j]
是否小于a[i]
时,我们只需要元素的相对顺序。因此,我们将每个元素映射到排序元素数组中的索引,而不重复。例如,序列a = 2 18 6 7 8 2 10
将转换为b = 0 5 1 2 3 0 4
。这可以在O(n*log(n))
中完成。
b
的最大元素将小于n
,因此,我们可以在细分[0, n]
上构建细分树,每个节点都包含细分中的最大总和(我们需要两个分段树,相应地为“高”和“低”部分)。现在让我们描述算法的步骤i
:
max_low
上的最大金额[0, b[i]-1]
(最初树的所有节点都包含零)。F(i, high)
等于max_low + a[i]
。max_high
上找到最大金额[b[i]+1, n]
。F(i, low)
等于max_high + a[i]
。[b[i], b[i]]
段,F(i, high)
值重新计算父节点的最大值(以及[b[i], b[i]]
节点本身)。F(i, low)
执行相同操作。复杂性分析:b
序列计算为O(n*log(n))
。细分树最大/更新操作的复杂度为O(log(n))
,其中有O(n)
个。该算法的总体复杂度为O(n*log(n))
。