使用Scala翻转硬币

时间:2012-05-01 18:40:24

标签: algorithm scala optimization data-structures iteration

我正在尝试解决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 <= N <= 100000 1 <= Q <= 100000 0 <= A <= B <= N - 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),显然这可以优化以在对数时间内解决。

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

由于

2 个答案:

答案 0 :(得分:2)

这些天我对scala了解不多,但我可以建议一个关于O(log(n))的更一般问题的答案。通常这种算法使用树,我认为你可以在这里使用。

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

这样就可以为一个命令提供O(n),为另一个命令提供O(log(n))。

但你可以做得更好。你也可以进行翻转操作O(log(n))。要做到这一点,你会向每个节点添加一个“翻转”标志。如果设置,那么该点下面的所有节点都被翻转。有一些记账细节,但总体思路就在那里。

如果你把它归结为合乎逻辑的结论,你实际上并不需要存储叶子,至少在开始时。您只需在处理命令时添加具有所需详细级别的节点。此时你基本上都有评论中提到的区间树。

答案 1 :(得分:0)

对此进行建模的一种简洁方法是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

这不一定是最优的,但它是一种相当有效的方法,因为你只是在翻转位。