我正在解决Snapper Chain问题,来到这里寻找一个关于位操作算法的解释,我在解决方案中遇到了它。我找到了一些好的信息,但是对于一个位运算新手来说,我仍然花了很长时间才弄明白它。
以下是我尝试解释该算法及其如何得出的过程。如果我们枚举链中每个快门的所有可能的电源和开/关状态,我们会发现一种模式。以测试用例N=3,K=7(3个快门,7次快照)为例,我们展示了每个kth快照的每个快门的电源和开/关状态:
1 2 3
0b:1 1 1.1 1.0 0.0 -> ON for n=1
0b:10 2 1.0 0.1 0.0
0b:11 3 1.1 1.1 1.0 -> ON for n=1, n=2
0b:100 4 1.0 0.0 1.0
0b:101 5 1.1 1.0 1.0 -> ON for n=1
0b:110 6 1.0 0.1 0.1
0b:111 7 1.1 1.1 1.1 -> ON for n=2, n=3
当所有的snappers都开启并接收电源时,或者我们有一个第k个快照导致n个1时,灯泡就会亮起来。更简单地说,只要所有的snappers都是ON状态,灯泡就会亮起来,因为它们都必须接收电源才能保持ON状态(从而点亮灯泡)。这意味着对于每个k次快照,我们需要n个1。
此外,您可以注意到,k不仅对于满足n=3的k=7是所有二进制1,而且对于满足n=2的k=3和满足n=1的k=1也是如此。此外,对于n = 1或2,我们看到打开灯泡的每个快照数,k的最后n位数字始终为1。我们可以尝试推广,所有满足n个snappers的ks都将是以n个1结尾的二进制数。
我们可以使用早期帖子中提到的表达式,即1 << n - 1总是给我们n个二进制1,或在这种情况下,1 << 3 - 1 = 0b111。如果我们将n个snappers的链视为每个数字表示开/关的二进制数,并且我们想要n个1,则这给出了我们的表示形式。
现在我们想要找到那些1 << n - 1等于以n个二进制位为结尾的k的情况,我们可以通过执行按位与操作来实现: k & (1 << n - 1)获取k的最后n位,然后将其与1 << n - 1进行比较。
我想这种思维方式在处理这些类型的问题后会更自然,但对我来说仍然很吓人,我怀疑我永远不会靠自己想出这样的解决方案!
这是我的Perl解决方案:
$tests = <>;
for (1..$tests) {
($n, $k) = split / /, <>;
$m = 1 << $n - 1;
printf "Case #%d: %s\n", $_, (($k & $m) == $m) ? 'ON' : 'OFF';
}
10000 - 1 = 9999
的计算过程,同样的过程也会出现在二进制中。 - Anycorn