【问题标题】:Sequence increasing and decreasing by turns序列依次递增和递减
【发布时间】:2017-11-18 21:53:48
【问题描述】:

假设我们有一个给定长度的整数序列n。我们想删除一些元素(可能没有),以便序列在结果中轮流增加和减少。这意味着,每个元素都应该具有比自身更大或都小于自身的相邻元素。 例如1 3 2 7 65 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))和正确的?

【问题讨论】:

  • 那么在“1 3 2 7 6”中,数字 3 有一个更大的邻居和一个更小的邻居吗?你的例子看起来像你的意思是“要么更大要么都更小”。

标签: algorithm sequence greedy


【解决方案1】:

对于索引为i 的序列中的每个元素,我们将计算F(i, high)F(i, low),其中F(i, high) 等于以i-th 元素结尾的具有所需特征的子序列的最大总和而这个元素是一个“高峰”。 (我将主要解释“高”部分,“低”部分可以类似地完成)。我们可以使用以下关系计算这些函数:

答案是所有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

  1. 使用“低”段树(最初树的所有节点都包含零)在段 [0, b[i]-1] 上找到最大和 max_low
  2. F(i, high) 等于 max_low + a[i]
  3. 使用“高”段树在段 [b[i]+1, n] 上找到最大的和 max_high
  4. F(i, low) 等于 max_high + a[i]
  5. F(i, high) 值更新“高”段树的[b[i], b[i]] 段,重新计算父节点(和[b[i], b[i]] 节点本身)的最大值。
  6. 对“低”段树和F(i, low) 执行相同操作。

复杂度分析:b序列计算为O(n*log(n))。段树最大/更新操作具有O(log(n)) 复杂性,其中有O(n)。该算法的整体复杂度为O(n*log(n))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    相关资源
    最近更新 更多