题目链接

质数取石子游戏

题目描述

有一个石子堆,包含 个石子。Alice 和 Bob 轮流取石子,Alice 先手。每次可以取 1 个或任意质数个石子。取走最后一个石子的玩家获胜。双方都采取最优策略,判断谁会赢。

解题思路

这是一个经典的公平博弈问题 (Impartial Game)。这类问题的核心是划分“必胜态”和“必败态”。

  • 必败态 (Losing Position, L-position): 在当前局面下,无论玩家如何操作,都会将一个“必胜态”留给对手。轮到自己时处于必败态,则必输。
  • 必胜态 (Winning Position, W-position): 在当前局面下,玩家至少有一种操作,可以将一个“必败态”留给对手。轮到自己时处于必胜态,则必胜。

我们的目标是找出所有必败态的规律。石子数为 0 是最基本的必败态。

我们可以通过分析少量石子的情况来寻找规律:

  • : 可取 1,剩 0 (L)。这是一个必胜态 (W)。Alice 胜
  • : 可取 2 (质数),剩 0 (L)。这是一个必胜态 (W)。Alice 胜
  • : 可取 3 (质数),剩 0 (L)。这是一个必胜态 (W)。Alice 胜
  • :
    • 取 1,剩 3 (W)。
    • 取 2 (质数),剩 2 (W)。
    • 取 3 (质数),剩 1 (W)。 所有操作都导向必胜态。这是一个必败态 (L)。Bob 胜

从这里我们发现第一个必败态是 4。这引导我们提出一个猜想:一个局面是必败态,当且仅当石子数量 是 4 的倍数

我们可以用数学归纳法来证明这个猜想:

  1. 基础情况: 对于 ,我们已经验证猜想是成立的。

  2. 归纳步骤: 假设对于所有小于 的数,猜想都成立。

    • 是 4 的倍数时 (): 我们需要证明这是一个必败态,即任何操作都会导向一个必胜态 (非 4 的倍数)。

      • 取 1 个石子:剩下
      • 取 2 个石子 (唯一的偶质数):剩下
      • 取任意奇数质数 :剩下 是偶数, 是奇数, 是奇数,必不为 4 的倍数。 因此,从一个 4 的倍数的局面出发,任何操作后剩余的石子数都不是 4 的倍数。根据归纳假设,这些都是必胜态。所以, 是必败态。
    • 不是 4 的倍数时: 我们需要证明这是一个必胜态,即至少存在一种操作,可以导向一个必败态 (4 的倍数)。

      • : 取 1 个石子,剩下
      • : 取 2 个石子 (质数),剩下
      • : 取 3 个石子 (质数),剩下 。 因此,从一个非 4 的倍数的局面出发,总能找到一种方法,使得剩余石子数是 4 的倍数。根据归纳假设,这是一个必败态。所以, 是必胜态。

最终结论: 猜想成立。Alice 作为先手,她获胜的条件是初始石子数 不是 4 的倍数。

  • 如果 Alice 获胜
  • 如果 Bob 获胜

代码

#include <iostream>

using namespace std;

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        if (n % 4 == 0) {
            cout << "Bob" << endl;
        } else {
            cout << "Alice" << endl;
        }
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            int n = sc.nextInt();
            if (n % 4 == 0) {
                System.out.println("Bob");
            } else {
                System.out.println("Alice");
            }
        }
    }
}
import sys

def solve():
    t_str = sys.stdin.readline()
    if not t_str:
        return
    t = int(t_str)
    for _ in range(t):
        n_str = sys.stdin.readline()
        if not n_str:
            break
        n = int(n_str)
        if n % 4 == 0:
            print("Bob")
        else:
            print("Alice")

solve()

算法及复杂度

  • 算法: 博弈论、数论
  • 时间复杂度: 对于每组测试数据,我们只进行了一次取模和判断操作,时间复杂度为 。总的时间复杂度为 ,其中 是测试数据的组数。
  • 空间复杂度: 没有使用额外的存储空间,空间复杂度为