You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 894. 拆分-Nim游戏

一、题目描述

给定 n 堆石子,两位玩家轮流操作,每次操作可以取走其中的一堆石子,然后放入两堆 规模更小 的石子(新堆规模可以为 0,且两个新堆的石子总数可以大于取走的那堆石子数),最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

输入格式 第一行包含整数 n

第二行包含 n 个整数,其中第 i 个整数表示第 i 堆石子的数量 a_i

输出格式 如果先手方必胜,则输出 Yes

否则,输出 No

数据范围 1≤n,a_i≤100

输入样例

2
2 3

输出样例

Yes

二、解题思路

相比于集合-Nim,这里的每一堆可以变成不大于原来那堆的任意大小的两堆

a[i]可以拆分成(b[i],b[j]),为了避免重复规定b[i]>=b[j],即:a[i]>b[i]>=b[j]

相当于一个局面拆分成了两个局面,由SG函数理论:多个独立局面的SG值,等于这些局面SG值的 异或和

因此需要存储的状态就是sg(b[i])^sg(b[j])(与集合-Nim的唯一区别)。

三、实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 110;

int n;
int f[N];
int res;

int sg(int x) {
    if (~f[x]) return f[x];
    unordered_set<int> S;
    
    // 所有能到的局面
    for (int i = 0; i < x; i++)
        for (int j = 0; j <= i; j++)
            S.insert(sg(i) ^ sg(j));

    for (int i = 0;; i++)
        if (!S.count(i)) return f[x] = i;
}

int main() {
    // 初始化
    memset(f, -1, sizeof f);
    cin >> n;
    while (n--) {
        int x;
        cin >> x;
        res ^= sg(x);
    }
    if (res)
        puts("Yes");
    else
        puts("No");
    return 0;
}