【问题标题】:Flipping coins using Scala使用 Scala 翻转硬币
【发布时间】:2012-05-11 06:17:31
【问题描述】:

我正在尝试解决 scala 中 codechef 的翻转硬币问题。问题陈述如下:

桌上有 N 个硬币,编号从 0 到 N - 1。 最初,每枚硬币都是反面朝上的。您必须执行两种类型的 操作: 1) 翻转所有编号在 A 和 B 之间的硬币。这是 由命令“0 A B”表示 2) 回答多少硬币编号 A和B之间是单挑的。这由命令“1 A B"。输入:第一行包含两个整数,N 和 Q。 接下来的 Q 行是前面提到的“0 A B”或“1 A B”的形式 以上。

输出:为“1 A B”形式的每个查询输出 1 行 包含相应查询所需的答案。

示例输入:

 4 7
1 0 3
0 1 2
1 0 1
1 0 0
0 0 3
1 0 3 
1 3 3

样本输出:

0
1
0
2
1

约束:1

以最简单的方式,我正在考虑在 scala 中初始化一个 Ints 数组,如下所示:

 var coins = new Array[Int](1000)

如果遇到命令0 A B,我会简单的设置A的索引直到B+1为1,如下:

 for(i <- 5 until 8){
      coins(i) = 1
    }

如果我遇到命令 1 A B,我将从 A 到 B+1 取出数组的一个切片,并计算给定切片中 1 的数量,我将按如下方式执行:

val headCount = coins.slice(5,8).count(x => x == 1)

似乎这个操作在最坏的情况下需要 O(n),显然这可以优化以在对数时间内解决。

谁能指出我在这里可能做错了什么以及如何以最佳方式解决这个问题。

谢谢

【问题讨论】:

  • 而不是数组来存储可以找到磁头的间隔。请参阅区间树 wiki 文章,了解如何实现函数。
  • @Elkamina - 谢谢。也许在这种特殊情况下,scala 中的区间树实现会起作用。需要阅读更多内容。

标签: algorithm scala optimization data-structures iteration


【解决方案1】:

这些天我对 scala 了解不多,但我可以为关于 O(log(n)) 的更一般的问题提出一个答案。通常此类算法使用树,我认为您可以在这里这样做。

如果您构建一棵平衡树,将硬币作为叶子,那么您可以在每个节点中存储硬币的总数以及该节点下方叶子中的正面数量。您可以想象翻转硬币的代码从节点信息中计算出要访问的叶子,并在 O(n) 时间内工作(您仍然需要翻转硬币)。但是如果翻转代码也更新了节点数据,那么正面的数量将是 O(log(n)),因为您可以使用节点信息 - 您不需要去叶子。

这样一个命令的 O(n) 和另一个命令的 O(log(n))。

但你可以做得更好。我认为你也可以进行翻转操作 O(log(n))。为此,您将向每个节点添加一个“翻转”标志。如果设置,则该点以下的所有节点都被翻转。有一些记账细节,但大体思路就在那里。

如果您将此得出合乎逻辑的结论,那么您实际上不需要存储叶子,至少在开始时是这样。您只需在处理命令时添加具有所需详细程度的节点。至此,您基本上有了 cmets 中提到的区间树。

【讨论】:

  • 谢谢。是的,看来我需要努力改进红黑树的版本,以在对数时间内解决上述问题。
  • 好吧,如果你想避免最坏情况下的问题,你需要一个平衡树,而红黑树是平衡树的一个很好的实现。如果您没有找到类似的东西,可以快速搜索 devdaily.com/java/jwarehouse/scala/src/library/scala/collection/…
  • ps 我建议首先不要太担心平衡树。为不平衡的树正确记账会容易得多(当然更容易调试)。一旦你有这个工作,然后让它平衡(重新平衡的过程将需要你在标志和计数上玩弄 - 如果你已经知道不平衡的代码可以工作,这将是棘手的,而且更容易)。
  • 感谢详细的算法分解和示例。我会将此标记为答案。
【解决方案2】:

对此进行建模的一种简洁方法是BitSet,其中集合中的整数值表示棋盘上正面的索引。然后你可以在这样的范围内翻转硬币:

def flip(coins: BitSet, a: Int, b: Int) = coins ^ BitSet(a to b: _*)

你可以类似地计算一个范围内的正面:

def heads(coins: BitSet, a: Int, b: Int) = (coins & BitSet(a to b: _*)).size

或者(可能更快的)可变java.util.BitSet 版本:

import java.util.BitSet

def flip(coins: BitSet, a: Int, b: Int) { coins.flip(a, b + 1) }
def heads(coins: BitSet, a: Int, b: Int) = coins.get(a, b + 1).cardinality

这不一定是最佳的,但它是一种相当有效的方法,因为您只是在翻转比特。

【讨论】:

  • 谢谢。语句 BitSet(a to b: _*) 有什么作用?
猜你喜欢
  • 2018-12-30
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-14
相关资源
最近更新 更多