题解:AcWing 893 集合-Nim游戏
【题目来源】
AcWing:893. 集合-Nim游戏 - AcWing题库
【题目描述】
给定 \(n\) 堆石子以及一个由 \(k\) 个不同正整数构成的数字集合 \(S\)。
现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 \(S\),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
【输入】
第一行包含整数 \(k\),表示数字集合 \(S\) 中数字的个数。
第二行包含 \(k\) 个整数,其中第 \(i\) 个整数表示数字集合 \(S\) 中的第 \(i\) 个数 \(s_i\)。
第三行包含整数 \(n\)。
第四行包含 \(n\) 个整数,其中第 \(i\) 个整数表示第 \(i\) 堆石子的数量 \(h_i\)。
【输出】
如果先手方必胜,则输出 Yes。
否则,输出 No。
【输入样例】
2
2 5
3
2 4 7
【输出样例】
Yes
【算法标签】
《AcWing 893 集合-Nim游戏》 #数学知识# #博弈论# #SG函数#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 110, M = 10005; // 定义常量 N 和 M
int n, k; // n 表示石子的堆数,k 表示每次可以取的石子数的种类数
int s[N]; // s 数组存储每次可以取的石子数
int f[M]; // f 数组用于记忆化搜索,存储每个状态的 SG 值
// 计算 SG 值
int sg(int x)
{
if (f[x] != -1) return f[x]; // 如果已经计算过,直接返回
set<int> S; // S 用于存储所有可能的 SG 值
for (int i = 0; i < k; i++) { // 遍历所有可能的取石子数
if (x >= s[i]) S.insert(sg(x - s[i])); // 递归计算 SG 值并插入 S
}
for (int i = 0; i <= x; i++) { // 找到最小的未出现在 S 中的非负整数
if (S.count(i) == 0) return f[x] = i; // 返回该整数作为 SG 值
}
}
int main()
{
cin >> k; // 输入每次可以取的石子数的种类数 k
for (int i = 0; i < k; i++) cin >> s[i]; // 输入每次可以取的石子数
cin >> n; // 输入石子的堆数 n
memset(f, -1, sizeof(f)); // 初始化 f 数组为 -1(表示未计算)
int ans = 0; // 初始化异或结果为 0
for (int i = 0; i < n; i++) { // 遍历每堆石子
int x; // 定义变量 x,表示当前堆的石子数
cin >> x; // 输入当前堆的石子数
ans ^= sg(x); // 计算当前堆的 SG 值并与异或结果进行异或
}
if (ans) puts("Yes"); // 如果异或结果不为 0,输出 Yes
else puts("No"); // 否则输出 No
return 0; // 程序结束
}
【运行结果】
2
2 5
3
2 4 7
Yes
浙公网安备 33010602011771号