抽象游戏的最佳策略


12

在一次采访中,我遇到了以下问题(我已经解决不了,没有试图欺骗自己):游戏从一个正整数。(例如)此数字转换为二进制表示形式,而是设置为的位数。(例如,)A0A0=1234N1A0=b100 1101 0010N=5.

玩家1选择一个比小的数字。只能将一位设置为1。(例如)令。(例如)如果满足先前的约束,并且设置的位数仍然等于N,则移动有效B0A0B0B0=b10 0000 0000=512A1=A0B0A1=1234512=722=b1011010010B0A1

玩家2通过选择有效的从继续,然后玩家1从继续,依此类推。如果没有有效的动作,则玩家将输。A1B1A2

假设两个玩家都发挥最佳状态,则使用合理有效的方法确定获胜玩家。(在我的问题定义中,对此的限制是程序必须能够为容纳有符号的32位整数的数百万个输入数字提供解决方案。)也就是说,解决方案不需要完全分析。


我个人的兴趣是弄清楚我对找到并实施正确解决方案的期望是否合理,而在给出的120分钟内却没有关于正确性的反馈。或者这是那些“让我们看看他们之前是否曾经看过这个难题”之一。

我失败了,因为我选择实施一种看似合理的策略,这给了我一些预先给出的测试用例正确的结果,浪费了太多时间使它快速运行,最终导致错误的处理我的时间用完了。

回想起来,我应该对小数目的起始号码实施强力搜索并记住部分解决方案,但是事后看来总是20/20。我很好奇,但是是否有一种另类的通用方法使我无法成为一个蓬松的人。


从描述中,我不了解所选的动作必须将单个位设置为1(我认为这只是示例的一部分)。
jjmontes

@jjmontes-这是选择B号的第一条规则-所有示例均已指明,括号之外的所有内容都是通用的。您有什么建议可以更清楚吗?
millimoose

1
也许“玩家1只选择一个数比较小,它必须只有一个位设置为1”?(也许只是我一个人,但我必须阅读@orlp答案才能意识到这是一个约束)。A 0B0A0
jjmontes

@Veedrac-伙计,我知道,我为使位计数运行得相当快而付出的所有努力不会浪费。解释一个答案,为什么它的工作原理是优秀的。
millimoose

@millimoose Bitcount适用于大多数现代CPU的硬件!
Veedrac

Answers:


21

花点时间让自己意识到,如果我们只能减去2的幂,并且popcount不能改变,我们必须在其他数字为的位置减去。其结果始终是该位置的,并且该数字在其他任何地方都没有变化。10 01011001

换句话说,该游戏是一系列的交换,如果所有的交换都在右侧,则游戏结束。请注意,该游戏不可能提前结束-您不会陷入困境。您将总是以所有零在左边,而所有零在右边的位置结束。1001

因此,游戏中唯一的决定性因素是要达到所有人都处于正确状态的状态需要进行多少次交换,并且没有成败的策略。唯一的决定因素是所用掉期数量的奇偶性。

那么,需要进行多少次交换?请注意, s不能彼此交叉,因此如果我们对它们进行编号并通过交换对其进行跟踪,则它们在最终状态下将保持相同的顺序。每次互换使他们更接近最终位置。1

因此,如果第个(从右数起,最右边的是第个)位于右数位置,则需要交换以到达其正确位置。这为我们提供了一种算法,可以计算所需的掉期数量:1 1 0 1 k k ii1101kki

i = 0
k = 0
total = 0
while n > 0:
    if n & 1:
       total += k - i
       i += 1
    n >>= 1
    k += 1

现在我们可以看看total谁赢了的平价。时间复杂度为。O(logn)


这似乎是正确的,上交后我偶然发现了这种方法的点点滴滴。猜猜我已经做好了准备,开始编写代码时就跳了枪,陷入了后来的大雁追逐中。
millimoose

考虑到这一点,该策略无关紧要的事实意味着我要么在实现中遇到了一个相当模糊的错误,要么应该产生了与其他任何可以正确玩游戏的实现相同的结果……
millimoose

5

解决此问题的一种方法如下:

  • 使用您建议的“记忆式蛮力”方法找到一些简单值的解决方案。

  • 猜猜答案(哪些职位赢了,哪些职位输了)。

  • 尝试证明您的答案。如果成功,那就太好了。否则,尝试找到一个反例,并用它来猜测另一个答案。在这里解决更多的情况可能会有帮助。

真的很难说需要多少时间。但是,在面试中不一定要找到解决方案。相反,面试官想知道你是如何接近解决问题,你设法让哪些进展。


是的,他们拒绝了我,因为我的输出是错误的,我已经没时间了。
millimoose

记忆力强的方法本来是正确的,因为它在策略上没有捷径可走。但是,我认为,它的运行速度也令人难以置信,并且如果不使用无用的内存,则记忆可能是一项繁重的工作,可能对帮助没有多大帮助。也许不是,我稍后会尝试将其清除出系统。
millimoose

5

请注意@orlp的答案,我们希望从起始位置到结束位置的位移总和为奇偶性。让我们注释一下:

       9876543210
       9 76 4  1    (positions at start)
start: 1011010010
end:   0000011111
            43210   (positions at end)

所以我们要

  ((1 - 0) + (4 - 1) + (6 - 2) + (7 - 3) + (9 - 4)) & 1
= ((1 + 4 + 6 + 7 + 9) - (0 + 1 + 2 + 3 + 4)) & 1
= ((1 + 0 + 0 + 1 + 1) - (0 + 1 + 0 + 1 + 0)) & 1

第一部分只是奇数位置位数的奇偶校验。您可以通过采用最大无符号整数,除以0b11并取反来掩盖它。

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (0 + 1 + 0 + 1 + 0)) & 1

第二部分是的奇偶一半中的比特的数量x

= (bitcount(x & ~(UINT_MAX / 0b11)) ^ (bitcount(x) >> 1)) & 1

bitcount既可以使用硬件popcnt指令,也可以手动实现,只需使用最后一位或倒数第二位,这样就可以快速减少数据量

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.