我正在尝试解决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),显然这可以优化以在对数时间内解决。
有人可以指出我在这里可能做错了什么,以及如何以最佳方式解决这个问题。
由于
答案 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
这不一定是最优的,但它是一种相当有效的方法,因为你只是在翻转位。