此类问题的最佳方法。
首先你需要对博弈论有基本的了解。真的很基础。那就是你意识到这样一个事实,对于给定的数字 N,要么是先发玩家的获胜策略,要么是对手的获胜策略。因此,您必须假设他们都知道策略并尽其所能采取最佳行动。
然后你开始熟悉这个游戏。你练习的水平很低。您很快就会注意到,对于 2-9,先发者获胜,而对于 10-18,他必须输。因此,您的函数已准备好用于N<=18
.
然后你开始思考一般的制胜策略。了解策略将为您提供算法。5 分钟后(越快越好),您会明白自己无法及时找到获胜策略,因为在这种情况下并不明显。所以你决定给计算机一些基本原理,让它为你解谜。你知道你会使用递归。
你试图找到递归的规则。您可能希望从头开始或从头开始。我将从最后描述这种方法。
游戏的目标是把你的对手推到区域,他必须给你胜利。不要自己被推到那个区域。从 N/9 到 N 有一个获胜区。如果一个人在 N/9/2 和 N/9 之间被逼上场,他必须输。(因为他的两个动作都将他的对手推到了胜利区。)所以你写你的函数:
wins(n) {
// returns 1, if starting player is able to reach
// the range [n, 2*n) with his move
if (n<=18) {
if (n<=9)
return 1;
else
return 0;
} else {
// I must push him to the zone between N/9/2 and N/9
return wins(n/18);
}
如果你达到了那个点,你就通过了。还有一些细节,比如是使用 float 还是 int,是否使用 int 向上舍入或向下舍入。但总的来说,你展示了正确的思维方式,你已经准备好面对面试官了:)
编辑:实际上上面的代码有一个错误。“获胜”与“能够达到范围(n,2n)”不同。这里可能需要 2 个函数:wins(n)
和reaches_slightly_above(n)
. 后者将以递归方式调用,低于 18 的返回值应该不同,类似于Peter de Rivaz解决方案中的值。但是,下面的解决方案和一般方法应该没问题。
从下到上的替代方法是使用以下功能:
wins(a,n) {
if (9*a >= n)
// I win easily
return 1;
else
// if one of my moves pushes the opponent to the zone
// where he's not able to win, then I win!
return !wins(2*a, n) || !wins(9*a, n);
}
如果他们要求n
,您返回 的值win(1,n)
。这个算法的复杂性并不明显,但我相信它是对数的。