2025-06-10:移除石头游戏。用go语言,Alice 和 Bob 玩一个拿石头的游戏,规则如下:
-
他们轮流从一堆石头中拿走石头,Alice 先手。
-
Alice 第一次拿走的石头数量固定为 10 个。
-
之后每次轮到某个玩家时,他必须拿走的石头数量等于对方上一次拿走石头数减 1。
-
如果某个玩家无法按规则拿足够的石头,则这个玩家输掉游戏。
给定初始石头总数 n,如果 Alice 最终获胜,返回 true,否则返回 false。
1 <= n <= 50。
输入:n = 12。
输出:true。
解释:
Alice 第一次操作中移除 10 个石头,剩下 2 个石头给 Bob 。
Bob 无法移除 9 个石头,所以 Alice 赢下游戏。
题目来自力扣3360。
一般情况分析
我们需要找到一个通用的方法来判断 Alice 是否能获胜。以下是游戏的步骤:
- Alice 第一次拿走 10 个石头,剩余
n - 10
个石头。 - Bob 需要拿走
10 - 1 = 9
个石头:- 如果
n - 10 < 9
,Bob 无法拿走 9 个,直接输掉游戏,Alice 获胜。 - 否则,Bob 拿走 9 个,剩余
n - 10 - 9 = n - 19
个石头。
- 如果
- Alice 需要拿走
9 - 1 = 8
个石头:- 如果
n - 19 < 8
,Alice 无法拿走 8 个,输掉游戏,Bob 获胜。 - 否则,Alice 拿走 8 个,剩余
n - 19 - 8 = n - 27
个石头。
- 如果
- Bob 需要拿走
8 - 1 = 7
个石头:- 如果
n - 27 < 7
,Bob 无法拿走 7 个,输掉游戏,Alice 获胜。 - 否则,Bob 拿走 7 个,剩余
n - 27 - 7 = n - 34
个石头。
- 如果
- 游戏继续,每次拿走的石头数量依次减少 1(从 10 开始,然后是 9、8、7、6、…),直到某个玩家无法按规则拿走足够的石头。
关键观察
游戏的关键在于:
- Alice 第一次拿走 10 个石头。
- 之后每次拿走的石头数量是对方上一次拿走的数量减 1。
- 游戏会在某个玩家无法按规则拿走足够的石头时结束。
我们需要找到 n
的临界值,使得 Alice 能够获胜。具体来说:
- 如果
n - 10 < 9
(即n < 19
),Alice 直接获胜。 - 如果
n - 10 - 9 < 8
(即n < 27
),Bob 会在第二轮获胜。 - 如果
n - 10 - 9 - 8 < 7
(即n < 34
),Alice 会在第三轮获胜。 - 以此类推。
数学推导
游戏结束的条件是:在某一轮中,剩余的石头不足以让当前玩家拿走所需的石头数量。
- 第一轮:Alice 拿走 10,剩余
n - 10
。- Bob 需要拿走 9,条件是
n - 10 >= 9
,即n >= 19
。
- Bob 需要拿走 9,条件是
- 第二轮:Bob 拿走 9,剩余
n - 19
。- Alice 需要拿走 8,条件是
n - 19 >= 8
,即n >= 27
。
- Alice 需要拿走 8,条件是
- 第三轮:Alice 拿走 8,剩余
n - 27
。- Bob 需要拿走 7,条件是
n - 27 >= 7
,即n >= 34
。
- Bob 需要拿走 7,条件是
- 第四轮:Bob 拿走 7,剩余
n - 34
。- Alice 需要拿走 6,条件是
n - 34 >= 6
,即n >= 40
。
- Alice 需要拿走 6,条件是
- 第五轮:Alice 拿走 6,剩余
n - 40
。- Bob 需要拿走 5,条件是
n - 40 >= 5
,即n >= 45
。
- Bob 需要拿走 5,条件是
- 第六轮:Bob 拿走 5,剩余
n - 45
。- Alice 需要拿走 4,条件是
n - 45 >= 4
,即n >= 49
。
- Alice 需要拿走 4,条件是
- 第七轮:Alice 拿走 4,剩余
n - 49
。- Bob 需要拿走 3,条件是
n - 49 >= 3
,即n >= 52
。 - 但
n
的最大值是 50,因此游戏最多进行到第六轮。
- Bob 需要拿走 3,条件是
胜负判断
Alice 获胜的条件是游戏在 Bob 的回合无法拿走足够的石头时结束。具体来说:
- 如果
n
满足n - 10 < 9
(n < 19
),Alice 直接获胜。 - 如果
n
满足n - 19 - 8 < 7
(n < 34
)且n >= 19
,Bob 在第三轮无法拿走 7,Alice 获胜。 - 如果
n
满足n - 34 - 6 < 5
(n < 45
)且n >= 34
,Bob 在第五轮无法拿走 5,Alice 获胜。 - 如果
n
满足n - 45 - 4 < 3
(n < 52
)且n >= 49
,Bob 在第七轮无法拿走 3,Alice 获胜。
因此,Alice 获胜的 n
的范围是:
n < 19
27 <= n < 34
40 <= n < 45
49 <= n <= 50
代码中的数学公式
代码中的 canAliceWin
函数使用了以下公式:
.
x := (21 - int(math.Ceil(math.Sqrt(float64(441 - n*8))))) / 2
return x%2 > 0
这个公式的推导如下:
- 我们需要找到一个
x
,使得n
落在 Alice 获胜的区间。 - Alice 获胜的
n
的临界点可以通过以下不等式描述:- 第一轮:
n < 19
- 第二轮:
n >= 19
且n - 19 < 8
(即n < 27
) - 第三轮:
n >= 27
且n - 27 < 7
(即n < 34
) - 以此类推。
- 第一轮:
- 可以观察到 Alice 获胜的
n
的区间是:[10, 18]
(n < 19
)[27, 33]
(n >= 27
且n < 34
)[40, 44]
(n >= 40
且n < 45
)[49, 50]
(n >= 49
且n < 52
)
- 这些区间的起始点是
10, 27, 40, 49
,可以表示为:- 第一区间:
10 = 10
- 第二区间:
27 = 10 + 9 + 8
- 第三区间:
40 = 10 + 9 + 8 + 7 + 6
- 第四区间:
49 = 10 + 9 + 8 + 7 + 6 + 5 + 4
- 第一区间:
- 这些起始点可以表示为:
S(k) = 10 + 9 + 8 + ... + (11 - k)
,其中k
是区间编号。
- 通过数学推导,可以找到一个关于
n
的二次不等式,从而得到x
的表达式。
时间复杂度和空间复杂度
- 时间复杂度:
O(1)
,因为公式计算是常数时间操作。 - 空间复杂度:
O(1)
,没有使用额外空间。
总结
- Alice 第一次拿走 10 个石头。
- 之后每次拿走的石头数量是对方上一次拿走的数量减 1。
- 游戏在某个玩家无法按规则拿走足够的石头时结束。
- Alice 获胜的条件是
n
落在特定的区间(n < 19
、27 <= n < 34
、40 <= n < 45
、49 <= n <= 50
)。 - 代码中的数学公式通过解二次不等式直接计算出 Alice 是否能获胜。
- 时间复杂度和空间复杂度均为
O(1)
。
Go完整代码如下:
.
package main
import (
"fmt"
"math"
)
func canAliceWin(n int) bool {
x := (21 - int(math.Ceil(math.Sqrt(float64(441-n*8))))) / 2
return x%2 > 0
}
func main() {
n := 12
fmt.Println(canAliceWin(n))
}
Python完整代码如下:
.
# -*-coding:utf-8-*-
import math
def can_alice_win(n: int) -> bool:
x = (21 - math.ceil(math.sqrt(441 - n * 8))) // 2
return x % 2 > 0
if __name__ == "__main__":
n = 12
print(can_alice_win(n))